Repository: aksiksi/compose2nix
Pull request #80 (2025-03-02 12:23:08)

Adds nix option support
This PR adds an option, that allows the auto generation of a nix option with a user defined prefix.
If the option is not set, then the behaviour is as before.

I'm using Nix on multiple hosts with the same config and this change enables me to simply enable the containers only on the hosts that need them. 

Example:
`./compose2nix -option_prefix custom.cont -write_nix_setup=false`

This results in the following (I removed the inner contents for readability)
```nix
# Auto-generated using compose2nix v0.3.2-pre.
{ pkgs, lib, config, ... }:

{options.custom.cont.paperless-ai = lib.mkOption {
    type = lib.types.bool;
    default = lib.mkDefault false;
    description = "Enable paperless-ai";
  };

  config = lib.mkMerge [
    (lib.mkIf config.custom.cont.paperless-ai {


  # Containers
  virtualisation.oci-containers.containers."paperless-ai" = {
    ...
  };
  systemd.services."podman-paperless-ai" = {
    ...
  };

  # Volumes
  systemd.services."podman-volume-paperless-ai_data" = {
    ...
  };

  # Root service
  # When started, this will automatically create all resources and start
  # the containers. When stopped, this will teardown all resources.
  systemd.targets."podman-compose-paperless-ai-root" = {
    ...
  };})
  ];  
}

```

The formatting isn't ideal, but it works. I'm new at programming go, so if there is come way to improve this, please do. Otherwise I'm happy to use it as it is.

Nivalux: Now I tested it in my live system and it works. Had to fix the format, in order to preserve the current tests.
aksiksi: Thank you for the PR! 

Three general comments before I review:

1. How about two new flags:
    * `enable_option` (default: `false`): If set, we add a `enable` option to the generated NixOS module
    * `enable_option_prefix` (default: empty): If set, we'll add a prefix to the option name. If empty, defaults to Compose project name

2. Please add a unit test to `nix_test.go`. Example: https://github.com/aksiksi/compose2nix/blob/8941b4f4c1256ea38a85db88e423e53a9104e4d7/nix_test.go#L108

3. Please modify the NixOS integration test to use this feature.
    1. Pass the new flags to the Podman `compose2nix` call here: https://github.com/aksiksi/compose2nix/blob/8941b4f4c1256ea38a85db88e423e53a9104e4d7/nixos-test/update.sh#L15
    2. Then make sure to enable the option in `test.nix` here: https://github.com/aksiksi/compose2nix/blob/8941b4f4c1256ea38a85db88e423e53a9104e4d7/nixos-test/test.nix#L39
Nivalux: Thank you for the quick response!
One question about your first point:
If only `enable_option` is set, then it should create an option named `options.PROJECT_NAME`, right?
And if the enable_option_prefix is set, then it should be `options.PREFIX.PROJECT_NAME`.

Or do you prefer, we also add `enable` to the option, like `options.PREFIX.PROJECT_NAME.enable`?
aksiksi: Yes, let's define an explicit `enable` option. This also gives us room to define other options in the future.

And I think we can keep the prefix flag name as `option_prefix` instead since it's a prefix for all module options.

```nix
{
  # option_prefix not set
  options.PROJECT_NAME = {
    enable = lib.mkEnableOption "Enable {PROJECT_NAME}";
  };

  # option_prefix set
  options.OPTION_PREFIX.PROJECT_NAME = {
    enable = lib.mkEnableOption "Enable {PROJECT_NAME}";
  };
}
```
aksiksi: > Thank you for the PR!
> 
> Three general comments before I review:
> 
>     1. How about two new flags:
>        
>        * `enable_option` (default: `false`): If set, we add a `enable` option to the generated NixOS module
>        * `enable_option_prefix` (default: empty): If set, we'll add a prefix to the option name. If empty, defaults to Compose project name
> 
>     2. Please add a unit test to `nix_test.go`. Example: https://github.com/aksiksi/compose2nix/blob/8941b4f4c1256ea38a85db88e423e53a9104e4d7/nix_test.go#L108
> 
>     3. Please modify the NixOS integration test to use this feature.
>        
>        1. Pass the new flags to the Podman `compose2nix` call here: https://github.com/aksiksi/compose2nix/blob/8941b4f4c1256ea38a85db88e423e53a9104e4d7/nixos-test/update.sh#L15
>        2. Then make sure to enable the option in `test.nix` here: https://github.com/aksiksi/compose2nix/blob/8941b4f4c1256ea38a85db88e423e53a9104e4d7/nixos-test/test.nix#L39

Follow up on this - for the unit test, you can run the tests with `go test -update` to generate the golden/expected outputs for your new test.
Nivalux: I made the changes and added the unit and integration test
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/aksiksi/compose2nix/pull/80?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Assil+Ksiksi) Report
Attention: Patch coverage is `80.00000%` with `2 lines` in your changes missing coverage. Please review.
> Project coverage is 74.37%. Comparing base [(`8941b4f`)](https://app.codecov.io/gh/aksiksi/compose2nix/commit/8941b4f4c1256ea38a85db88e423e53a9104e4d7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Assil+Ksiksi) to head [(`76f9eaf`)](https://app.codecov.io/gh/aksiksi/compose2nix/commit/76f9eafd6a313cb1f4935ed6408c06e8ecf75722?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Assil+Ksiksi).

| [Files with missing lines](https://app.codecov.io/gh/aksiksi/compose2nix/pull/80?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Assil+Ksiksi) | Patch % | Lines |
|---|---|---|
| [main.go](https://app.codecov.io/gh/aksiksi/compose2nix/pull/80?src=pr&el=tree&filepath=main.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Assil+Ksiksi#diff-bWFpbi5nbw==) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/aksiksi/compose2nix/pull/80?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Assil+Ksiksi) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main      #80      +/-   ##
==========================================
- Coverage   75.40%   74.37%   -1.04%     
==========================================
  Files           6        6              
  Lines        1106     1116      +10     
==========================================
- Hits          834      830       -4     
- Misses        229      245      +16     
+ Partials       43       41       -2     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/aksiksi/compose2nix/pull/80?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Assil+Ksiksi).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Assil+Ksiksi).

Nivalux: I added the requested changes, but I used the docker runtime to test the enable option, since it requires including `config` in the nix header.

Patch:
diff --git a/compose.go b/compose.go
index 7289752..b348f3b 100644
--- a/compose.go
+++ b/compose.go
@@ -128,6 +128,8 @@ type Generator struct {
 	DefaultStopTimeout      time.Duration
 	IncludeBuild            bool
 	GetWorkingDir           getWorkingDir
+	OptionPrefix            string
+	EnableOption            bool
 
 	serviceToContainerName map[string]string
 	rootPath               string
@@ -201,6 +203,13 @@ func (g *Generator) Run(ctx context.Context) (*NixContainerConfig, error) {
 		version = appVersion
 	}
 
+	var option string = ""
+	if g.OptionPrefix == "" {
+		option = g.Project.Name
+	} else {
+		option = fmt.Sprintf("%s.%s", g.OptionPrefix, g.Project.Name)
+	}
+
 	return &NixContainerConfig{
 		Version:          version,
 		Project:          g.Project,
@@ -214,6 +223,8 @@ func (g *Generator) Run(ctx context.Context) (*NixContainerConfig, error) {
 		WriteNixSetup:    !g.NoWriteNixSetup,
 		AutoFormat:       g.AutoFormat,
 		IncludeBuild:     g.IncludeBuild,
+		Option:           option,
+		EnableOption:     g.EnableOption,
 	}, nil
 }
 
diff --git a/main.go b/main.go
index 9aeac6b..99541ef 100644
--- a/main.go
+++ b/main.go
@@ -41,6 +41,8 @@ var defaultStopTimeout = flag.Duration("default_stop_timeout", defaultSystemdSto
 var build = flag.Bool("build", false, "if set, generated container build systemd services will be enabled.")
 var writeNixSetup = flag.Bool("write_nix_setup", true, "if true, Nix setup code is written to output (runtime, DNS, autoprune, etc.)")
 var autoFormat = flag.Bool("auto_format", false, `if true, Nix output will be formatted using "nixfmt" (must be present in $PATH).`)
+var optionPrefix = flag.String("option_prefix", "", "Prefix for the option. If empty, the project name will be used as the option name. (e.g. custom.containers)")
+var enableOption = flag.Bool("enable_option", false, "generate a NixOS module option. this allows you to enable or disable the generated module from within your NixOS config. by default, the option will be named \"options.[project_name]\", but you can add a prefix using the \"option_prefix\" flag.")
 var version = flag.Bool("version", false, "display version and exit")
 
 type OsGetWd struct{}
@@ -116,6 +118,8 @@ func main() {
 		DefaultStopTimeout:      *defaultStopTimeout,
 		IncludeBuild:            *build,
 		GetWorkingDir:           &OsGetWd{},
+		OptionPrefix:            *optionPrefix,
+		EnableOption:            *enableOption,
 	}
 	containerConfig, err := g.Run(ctx)
 	if err != nil {
diff --git a/nix.go b/nix.go
index 6e7942c..650f77d 100644
--- a/nix.go
+++ b/nix.go
@@ -284,6 +284,8 @@ type NixContainerConfig struct {
 	AutoFormat       bool
 	AutoStart        bool
 	IncludeBuild     bool
+	Option           string
+	EnableOption     bool
 }
 
 func (c *NixContainerConfig) String() string {
diff --git a/templates/main.nix.tmpl b/templates/main.nix.tmpl
index 46b1b41..69a933c 100644
--- a/templates/main.nix.tmpl
+++ b/templates/main.nix.tmpl
@@ -1,13 +1,22 @@
 {{- if .Version -}}
 # Auto-generated using compose2nix v{{.Version}}.
 {{end -}}
-{{- if eq (.Runtime | printf "%s") "podman" -}}
+{{- if or (eq (.Runtime | printf "%s") "podman") .EnableOption -}}
 { pkgs, lib, config, ... }:
 {{- else -}}
 { pkgs, lib, ... }:
 {{- end}}
 
 {
+{{- if .EnableOption }}
+  options.{{.Option}} = {
+    enable = lib.mkEnableOption "Enable {{.Project.Name}}";
+  };
+
+  config = lib.mkMerge [
+    (lib.mkIf config.{{.Option}}.enable {
+{{end -}}
+
   {{- if .WriteNixSetup}}
   # Runtime
   virtualisation.{{.Runtime}} = {
@@ -78,4 +87,8 @@
     {{- end}}
   };
   {{- end}}
+{{- if .EnableOption }}
+    })
+  ];
+{{- end}}
 }


Test patch:
diff --git a/nix_test.go b/nix_test.go
index 0b98356..e6a32d9 100644
--- a/nix_test.go
+++ b/nix_test.go
@@ -115,6 +115,41 @@ func TestProject(t *testing.T) {
 	runSubtestsWithGenerator(t, g)
 }
 
+func TestEnableOption(t *testing.T) {
+	composePath, envFilePath := getPaths(t, true)
+	g := &Generator{
+		Project:      NewProject("myproject"),
+		Inputs:       []string{composePath},
+		EnvFiles:     []string{envFilePath},
+		EnableOption: true,
+	}
+	runSubtestsWithGenerator(t, g)
+}
+
+func TestEnableOption_WithHeader(t *testing.T) {
+	composePath, envFilePath := getPaths(t, true)
+	g := &Generator{
+		Project:      NewProject("myproject"),
+		Inputs:       []string{composePath},
+		EnvFiles:     []string{envFilePath},
+		EnableOption: true,
+		WriteHeader:  true,
+	}
+	runSubtestsWithGenerator(t, g)
+}
+
+func TestOptionPrefix(t *testing.T) {
+	composePath, envFilePath := getPaths(t, true)
+	g := &Generator{
+		Project:      NewProject("myproject"),
+		Inputs:       []string{composePath},
+		EnvFiles:     []string{envFilePath},
+		OptionPrefix: "custom.containers",
+		EnableOption: true,
+	}
+	runSubtestsWithGenerator(t, g)
+}
+
 func TestUnusedResources(t *testing.T) {
 	composePath, _ := getPaths(t, false)
 	g := &Generator{
diff --git a/nixos-test/docker-compose.nix b/nixos-test/docker-compose.nix
index 1f108f8..1209dfb 100644
--- a/nixos-test/docker-compose.nix
+++ b/nixos-test/docker-compose.nix
@@ -1,7 +1,14 @@
 # Auto-generated using compose2nix v0.3.2-pre.
-{ pkgs, lib, ... }:
+{ pkgs, lib, config, ... }:
 
 {
+  options.custom.prefix.myproject = {
+    enable = lib.mkEnableOption "Enable myproject";
+  };
+
+  config = lib.mkMerge [
+    (lib.mkIf config.custom.prefix.myproject.enable {
+
   # Runtime
   virtualisation.docker = {
     enable = true;
@@ -270,4 +277,6 @@
     };
     wantedBy = [ "multi-user.target" ];
   };
+    })
+  ];
 }
diff --git a/nixos-test/test.nix b/nixos-test/test.nix
index fb6bc67..8f70e5b 100644
--- a/nixos-test/test.nix
+++ b/nixos-test/test.nix
@@ -21,28 +21,37 @@ in
 {
   name = "basic";
   nodes = {
-    docker = { pkgs, lib, ... }: {
-      imports = [
-        ./docker-compose.nix
-      ];
-      # Override restart value and ensure it takes effect.
-      systemd.services."docker-service-b" = {
-        serviceConfig = {
-          Restart = lib.mkForce "on-success";
+    docker =
+      { pkgs, lib, ... }:
+      {
+        imports = [
+          ./docker-compose.nix
+        ];
+
+        custom.prefix.myproject.enable = true;
+
+        # Override restart value and ensure it takes effect.
+        systemd.services."docker-service-b" = {
+          serviceConfig = {
+            Restart = lib.mkForce "on-success";
+          };
         };
-      };
-    } // common;
-    podman = { pkgs, lib, ... }: {
-      imports = [
-        ./podman-compose.nix
-      ];
-      # Override restart value and ensure it takes effect.
-      systemd.services."podman-service-b" = {
-        serviceConfig = {
-          Restart = lib.mkForce "on-success";
+      }
+      // common;
+    podman =
+      { pkgs, lib, ... }:
+      {
+        imports = [
+          ./podman-compose.nix
+        ];
+        # Override restart value and ensure it takes effect.
+        systemd.services."podman-service-b" = {
+          serviceConfig = {
+            Restart = lib.mkForce "on-success";
+          };
         };
-      };
-    } // common;
+      }
+      // common;
   };
   # https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests
   testScript = ''
diff --git a/nixos-test/update.sh b/nixos-test/update.sh
index 56fc0a2..8d54ccb 100755
--- a/nixos-test/update.sh
+++ b/nixos-test/update.sh
@@ -11,7 +11,9 @@ bin/compose2nix \
   -check_systemd_mounts \
   -include_env_files=true \
   -generate_unused_resources=true \
-  -use_upheld_by
+  -use_upheld_by \
+  -option_prefix "custom.prefix" \
+  -enable_option=true
 bin/compose2nix \
   -runtime=podman \
   -inputs=nixos-test/compose.yml \
@@ -20,4 +22,3 @@ bin/compose2nix \
   -include_env_files=true \
   -generate_unused_resources=true \
   -use_upheld_by
-
diff --git a/testdata/TestEnableOption.docker.nix b/testdata/TestEnableOption.docker.nix
new file mode 100644
index 0000000..a52d234
--- /dev/null
+++ b/testdata/TestEnableOption.docker.nix
@@ -0,0 +1,357 @@
+{ pkgs, lib, config, ... }:
+
+{
+  options.myproject = {
+    enable = lib.mkEnableOption "Enable myproject";
+  };
+
+  config = lib.mkMerge [
+    (lib.mkIf config.myproject.enable {
+
+  # Runtime
+  virtualisation.docker = {
+    enable = true;
+    autoPrune.enable = true;
+  };
+  virtualisation.oci-containers.backend = "docker";
+
+  # Containers
+  virtualisation.oci-containers.containers."jellyseerr" = {
+    image = "docker.io/fallenbagel/jellyseerr:latest";
+    environment = {
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/var/volumes/jellyseerr:/app/config:rw"
+      "myproject_books:/books:rw"
+    ];
+    cmd = [ "ls" "-la" "/" ];
+    labels = {
+      "traefik.enable" = "true";
+      "traefik.http.routers.jellyseerr.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.jellyseerr.rule" = "Host(`requests.hello.us`)";
+      "traefik.http.routers.jellyseerr.tls.certresolver" = "htpc";
+    };
+    dependsOn = [
+      "myproject-sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--cpus=1.5"
+      "--dns=1.1.1.1"
+      "--health-cmd=curl -f http://localhost/\${POTATO}"
+      "--memory-reservation=524288000b"
+      "--memory=1048576000b"
+      "--network=container:myproject-sabnzbd"
+    ];
+  };
+  systemd.services."docker-jellyseerr" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "on-failure";
+      RestartMaxDelaySec = lib.mkOverride 90 "1m";
+      RestartSec = lib.mkOverride 90 "100ms";
+      RestartSteps = lib.mkOverride 90 9;
+    };
+    startLimitBurst = 3;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 120;
+    };
+    after = [
+      "docker-volume-myproject_books.service"
+    ];
+    requires = [
+      "docker-volume-myproject_books.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."myproject-sabnzbd" = {
+    image = "lscr.io/linuxserver/sabnzbd";
+    environment = {
+      "DOCKER_MODS" = "ghcr.io/gilbn/theme.park:sabnzbd";
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TP_DOMAIN" = "hey.hello.us\\/themepark";
+      "TP_HOTIO" = "false";
+      "TP_THEME" = "potato";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/var/volumes/sabnzbd:/config:rw"
+      "storage:/storage:rw"
+    ];
+    labels = {
+      "compose2nix.systemd.service.RuntimeMaxSec" = "10";
+      "compose2nix.systemd.unit.Description" = "This is the sabnzbd container!";
+      "traefik.enable" = "true";
+      "traefik.http.routers.sabnzbd.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.sabnzbd.rule" = "Host(`hey.hello.us`) && PathPrefix(`/sabnzbd`)";
+      "traefik.http.routers.sabnzbd.tls.certresolver" = "htpc";
+    };
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--health-cmd=curl -f http://localhost/"
+      "--hostname=sabnzbd"
+      "--network-alias=sabnzbd"
+      "--network=myproject_default"
+    ];
+  };
+  systemd.services."docker-myproject-sabnzbd" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "always";
+      RestartMaxDelaySec = lib.mkOverride 90 "1m";
+      RestartSec = lib.mkOverride 90 "100ms";
+      RestartSteps = lib.mkOverride 90 9;
+      RuntimeMaxSec = lib.mkOverride 90 10;
+    };
+    unitConfig = {
+      Description = lib.mkOverride 90 "This is the sabnzbd container!";
+    };
+    after = [
+      "docker-network-myproject_default.service"
+      "docker-volume-storage.service"
+    ];
+    requires = [
+      "docker-network-myproject_default.service"
+      "docker-volume-storage.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."photoprism-mariadb" = {
+    image = "docker.io/library/mariadb:10.9";
+    environment = {
+      "MARIADB_AUTO_UPGRADE" = "1";
+      "MARIADB_DATABASE" = "photoprism";
+      "MARIADB_INITDB_SKIP_TZINFO" = "1";
+      "MARIADB_PASSWORD" = "insecure";
+      "MARIADB_ROOT_PASSWORD" = "insecure";
+      "MARIADB_USER" = "photoprism";
+    };
+    volumes = [
+      "/var/volumes/photoprism-mariadb:/var/lib/mysql:rw"
+      "photos:/photos:rw"
+    ];
+    user = "1000:1000";
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--health-cmd=[\"curl\", \"-f\", \"http://localhost\"]"
+      "--health-interval=1m30s"
+      "--health-retries=3"
+      "--health-start-interval=5s"
+      "--health-start-period=40s"
+      "--health-timeout=10s"
+      "--network=host"
+    ];
+  };
+  systemd.services."docker-photoprism-mariadb" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "always";
+      RestartMaxDelaySec = lib.mkOverride 90 "1m";
+      RestartSec = lib.mkOverride 90 "100ms";
+      RestartSteps = lib.mkOverride 90 9;
+    };
+    startLimitBurst = 10;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 "infinity";
+    };
+    after = [
+      "docker-volume-photos.service"
+    ];
+    requires = [
+      "docker-volume-photos.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."torrent-client" = {
+    image = "docker.io/haugene/transmission-openvpn";
+    environment = {
+      "GLOBAL_APPLY_PERMISSIONS" = "false";
+      "LOCAL_NETWORK" = "192.168.0.0/16";
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TRANSMISSION_DHT_ENABLED" = "false";
+      "TRANSMISSION_DOWNLOAD_DIR" = "/storage/Downloads/transmission";
+      "TRANSMISSION_HOME" = "/config/transmission-home";
+      "TRANSMISSION_INCOMPLETE_DIR" = "/storage/Downloads/transmission/incomplete";
+      "TRANSMISSION_INCOMPLETE_DIR_ENABLED" = "true";
+      "TRANSMISSION_PEX_ENABLED" = "false";
+      "TRANSMISSION_SCRIPT_TORRENT_DONE_ENABLED" = "true";
+      "TRANSMISSION_SCRIPT_TORRENT_DONE_FILENAME" = "/config/transmission-unpack.sh";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/etc/localtime:/etc/localtime:ro"
+      "/var/volumes/transmission/config:/config:rw"
+      "/var/volumes/transmission/scripts:/scripts:rw"
+      "storage:/storage:rw"
+    ];
+    ports = [
+      "9091:9091/tcp"
+    ];
+    labels = {
+      "autoheal" = "true";
+      "compose2nix.settings.autoStart" = "false";
+      "traefik.enable" = "true";
+      "traefik.http.routers.transmission.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.transmission.rule" = "Host(`hey.hello.us`) && PathPrefix(`/transmission`)";
+      "traefik.http.routers.transmission.tls.certresolver" = "htpc";
+      "traefik.http.services.transmission.loadbalancer.server.port" = "9091";
+    };
+    dependsOn = [
+      "myproject-sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--add-host=abc:93.184.216.34"
+      "--add-host=abc:::1"
+      "--cap-add=NET_ADMIN"
+      "--device=/dev/net/tun:/dev/net/tun:rwm"
+      "--dns=8.8.4.4"
+      "--dns=8.8.8.8"
+      "--network-alias=my-torrent-client"
+      "--network-alias=transmission"
+      "--network=myproject_something"
+      "--no-healthcheck"
+      "--privileged"
+      "--shm-size=67108864"
+      "--sysctl=net.ipv6.conf.all.disable_ipv6=0"
+    ];
+  };
+  systemd.services."docker-torrent-client" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "on-failure";
+    };
+    startLimitBurst = 3;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 "infinity";
+    };
+    after = [
+      "docker-network-myproject_something.service"
+      "docker-volume-storage.service"
+    ];
+    requires = [
+      "docker-network-myproject_something.service"
+      "docker-volume-storage.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."traefik" = {
+    image = "docker.io/library/traefik";
+    environment = {
+      "CLOUDFLARE_API_KEY" = "yomama";
+      "CLOUDFLARE_EMAIL" = "aaa@aaa.com";
+    };
+    volumes = [
+      "/var/run/podman/podman.sock:/var/run/docker.sock:ro"
+      "/var/volumes/traefik:/etc/traefik:rw"
+    ];
+    ports = [
+      "80:80/tcp"
+      "443:443/tcp"
+    ];
+    labels = {
+      "compose2nix.systemd.service.Restart" = "'no'";
+      "compose2nix.systemd.unit.AllowIsolate" = "true";
+      "traefik.enable" = "true";
+      "traefik.http.routers.traefik.entrypoints" = "https";
+      "traefik.http.routers.traefik.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.traefik.rule" = "Host(`hey.hello.us`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))";
+      "traefik.http.routers.traefik.service" = "api@internal";
+      "traefik.http.routers.traefik.tls.certresolver" = "htpc";
+    };
+    dependsOn = [
+      "sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--network=container:sabnzbd"
+    ];
+  };
+  systemd.services."docker-traefik" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "no";
+    };
+    unitConfig = {
+      AllowIsolate = lib.mkOverride 90 true;
+    };
+  };
+
+  # Networks
+  systemd.services."docker-network-myproject_default" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStop = "docker network rm -f myproject_default";
+    };
+    script = ''
+      docker network inspect myproject_default || docker network create myproject_default
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+  systemd.services."docker-network-myproject_something" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStop = "docker network rm -f myproject_something";
+    };
+    script = ''
+      docker network inspect myproject_something || docker network create myproject_something --label=test-label=okay
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+
+  # Volumes
+  systemd.services."docker-volume-myproject_books" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      docker volume inspect myproject_books || docker volume create myproject_books --opt=device=/mnt/media/Books --opt=o=bind --opt=type=none
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+  systemd.services."docker-volume-photos" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      docker volume inspect photos || docker volume create photos --opt=device=/mnt/photos --opt=o=bind --opt=type=none --label=test-label=okay
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+  systemd.services."docker-volume-storage" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      docker volume inspect storage || docker volume create storage --opt=device=/mnt/media --opt=o=bind --opt=type=none
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+
+  # Root service
+  # When started, this will automatically create all resources and start
+  # the containers. When stopped, this will teardown all resources.
+  systemd.targets."docker-compose-myproject-root" = {
+    unitConfig = {
+      Description = "Root target generated by compose2nix.";
+    };
+  };
+    })
+  ];
+}
diff --git a/testdata/TestEnableOption.podman.nix b/testdata/TestEnableOption.podman.nix
new file mode 100644
index 0000000..132d254
--- /dev/null
+++ b/testdata/TestEnableOption.podman.nix
@@ -0,0 +1,358 @@
+{ pkgs, lib, config, ... }:
+
+{
+  options.myproject = {
+    enable = lib.mkEnableOption "Enable myproject";
+  };
+
+  config = lib.mkMerge [
+    (lib.mkIf config.myproject.enable {
+
+  # Runtime
+  virtualisation.podman = {
+    enable = true;
+    autoPrune.enable = true;
+    dockerCompat = true;
+  };
+
+  # Enable container name DNS for all Podman networks.
+  networking.firewall.interfaces = let
+    matchAll = if !config.networking.nftables.enable then "podman+" else "podman*";
+  in {
+    "${matchAll}".allowedUDPPorts = [ 53 ];
+  };
+
+  virtualisation.oci-containers.backend = "podman";
+
+  # Containers
+  virtualisation.oci-containers.containers."jellyseerr" = {
+    image = "docker.io/fallenbagel/jellyseerr:latest";
+    environment = {
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/var/volumes/jellyseerr:/app/config:rw"
+      "myproject_books:/books:rw"
+    ];
+    cmd = [ "ls" "-la" "/" ];
+    labels = {
+      "traefik.enable" = "true";
+      "traefik.http.routers.jellyseerr.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.jellyseerr.rule" = "Host(`requests.hello.us`)";
+      "traefik.http.routers.jellyseerr.tls.certresolver" = "htpc";
+    };
+    dependsOn = [
+      "myproject-sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--cpus=1.5"
+      "--dns=1.1.1.1"
+      "--health-cmd=curl -f http://localhost/\${POTATO}"
+      "--memory-reservation=524288000b"
+      "--memory=1048576000b"
+      "--network=container:myproject-sabnzbd"
+    ];
+  };
+  systemd.services."podman-jellyseerr" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "on-failure";
+      RestartSec = lib.mkOverride 90 "5s";
+    };
+    startLimitBurst = 3;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 120;
+    };
+    after = [
+      "podman-volume-myproject_books.service"
+    ];
+    requires = [
+      "podman-volume-myproject_books.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."myproject-sabnzbd" = {
+    image = "lscr.io/linuxserver/sabnzbd";
+    environment = {
+      "DOCKER_MODS" = "ghcr.io/gilbn/theme.park:sabnzbd";
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TP_DOMAIN" = "hey.hello.us\\/themepark";
+      "TP_HOTIO" = "false";
+      "TP_THEME" = "potato";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/var/volumes/sabnzbd:/config:rw"
+      "storage:/storage:rw"
+    ];
+    labels = {
+      "compose2nix.systemd.service.RuntimeMaxSec" = "10";
+      "compose2nix.systemd.unit.Description" = "This is the sabnzbd container!";
+      "traefik.enable" = "true";
+      "traefik.http.routers.sabnzbd.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.sabnzbd.rule" = "Host(`hey.hello.us`) && PathPrefix(`/sabnzbd`)";
+      "traefik.http.routers.sabnzbd.tls.certresolver" = "htpc";
+    };
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--health-cmd=curl -f http://localhost/"
+      "--hostname=sabnzbd"
+      "--network-alias=sabnzbd"
+      "--network=myproject_default"
+    ];
+  };
+  systemd.services."podman-myproject-sabnzbd" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "always";
+      RuntimeMaxSec = lib.mkOverride 90 10;
+    };
+    unitConfig = {
+      Description = lib.mkOverride 90 "This is the sabnzbd container!";
+    };
+    after = [
+      "podman-network-myproject_default.service"
+      "podman-volume-storage.service"
+    ];
+    requires = [
+      "podman-network-myproject_default.service"
+      "podman-volume-storage.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."photoprism-mariadb" = {
+    image = "docker.io/library/mariadb:10.9";
+    environment = {
+      "MARIADB_AUTO_UPGRADE" = "1";
+      "MARIADB_DATABASE" = "photoprism";
+      "MARIADB_INITDB_SKIP_TZINFO" = "1";
+      "MARIADB_PASSWORD" = "insecure";
+      "MARIADB_ROOT_PASSWORD" = "insecure";
+      "MARIADB_USER" = "photoprism";
+    };
+    volumes = [
+      "/var/volumes/photoprism-mariadb:/var/lib/mysql:rw"
+      "photos:/photos:rw"
+    ];
+    user = "1000:1000";
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--health-cmd=[\"curl\", \"-f\", \"http://localhost\"]"
+      "--health-interval=1m30s"
+      "--health-retries=3"
+      "--health-start-period=40s"
+      "--health-startup-interval=5s"
+      "--health-timeout=10s"
+      "--network=host"
+    ];
+  };
+  systemd.services."podman-photoprism-mariadb" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "always";
+      RestartSec = lib.mkOverride 90 "3m0s";
+    };
+    startLimitBurst = 10;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 "infinity";
+    };
+    after = [
+      "podman-volume-photos.service"
+    ];
+    requires = [
+      "podman-volume-photos.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."torrent-client" = {
+    image = "docker.io/haugene/transmission-openvpn";
+    environment = {
+      "GLOBAL_APPLY_PERMISSIONS" = "false";
+      "LOCAL_NETWORK" = "192.168.0.0/16";
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TRANSMISSION_DHT_ENABLED" = "false";
+      "TRANSMISSION_DOWNLOAD_DIR" = "/storage/Downloads/transmission";
+      "TRANSMISSION_HOME" = "/config/transmission-home";
+      "TRANSMISSION_INCOMPLETE_DIR" = "/storage/Downloads/transmission/incomplete";
+      "TRANSMISSION_INCOMPLETE_DIR_ENABLED" = "true";
+      "TRANSMISSION_PEX_ENABLED" = "false";
+      "TRANSMISSION_SCRIPT_TORRENT_DONE_ENABLED" = "true";
+      "TRANSMISSION_SCRIPT_TORRENT_DONE_FILENAME" = "/config/transmission-unpack.sh";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/etc/localtime:/etc/localtime:ro"
+      "/var/volumes/transmission/config:/config:rw"
+      "/var/volumes/transmission/scripts:/scripts:rw"
+      "storage:/storage:rw"
+    ];
+    ports = [
+      "9091:9091/tcp"
+    ];
+    labels = {
+      "autoheal" = "true";
+      "compose2nix.settings.autoStart" = "false";
+      "traefik.enable" = "true";
+      "traefik.http.routers.transmission.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.transmission.rule" = "Host(`hey.hello.us`) && PathPrefix(`/transmission`)";
+      "traefik.http.routers.transmission.tls.certresolver" = "htpc";
+      "traefik.http.services.transmission.loadbalancer.server.port" = "9091";
+    };
+    dependsOn = [
+      "myproject-sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--add-host=abc:93.184.216.34"
+      "--add-host=abc:::1"
+      "--cap-add=NET_ADMIN"
+      "--device=/dev/net/tun:/dev/net/tun:rwm"
+      "--dns=8.8.4.4"
+      "--dns=8.8.8.8"
+      "--network-alias=transmission"
+      "--network=myproject_something:alias=my-torrent-client"
+      "--no-healthcheck"
+      "--privileged"
+      "--shm-size=67108864"
+      "--sysctl=net.ipv6.conf.all.disable_ipv6=0"
+    ];
+  };
+  systemd.services."podman-torrent-client" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "on-failure";
+    };
+    startLimitBurst = 3;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 "infinity";
+    };
+    after = [
+      "podman-network-myproject_something.service"
+      "podman-volume-storage.service"
+    ];
+    requires = [
+      "podman-network-myproject_something.service"
+      "podman-volume-storage.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."traefik" = {
+    image = "docker.io/library/traefik";
+    environment = {
+      "CLOUDFLARE_API_KEY" = "yomama";
+      "CLOUDFLARE_EMAIL" = "aaa@aaa.com";
+    };
+    volumes = [
+      "/var/run/podman/podman.sock:/var/run/docker.sock:ro"
+      "/var/volumes/traefik:/etc/traefik:rw"
+    ];
+    ports = [
+      "80:80/tcp"
+      "443:443/tcp"
+    ];
+    labels = {
+      "compose2nix.systemd.service.Restart" = "'no'";
+      "compose2nix.systemd.unit.AllowIsolate" = "true";
+      "traefik.enable" = "true";
+      "traefik.http.routers.traefik.entrypoints" = "https";
+      "traefik.http.routers.traefik.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.traefik.rule" = "Host(`hey.hello.us`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))";
+      "traefik.http.routers.traefik.service" = "api@internal";
+      "traefik.http.routers.traefik.tls.certresolver" = "htpc";
+    };
+    dependsOn = [
+      "sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--network=container:sabnzbd"
+    ];
+  };
+  systemd.services."podman-traefik" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "no";
+    };
+    unitConfig = {
+      AllowIsolate = lib.mkOverride 90 true;
+    };
+  };
+
+  # Networks
+  systemd.services."podman-network-myproject_default" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStop = "podman network rm -f myproject_default";
+    };
+    script = ''
+      podman network inspect myproject_default || podman network create myproject_default
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+  systemd.services."podman-network-myproject_something" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStop = "podman network rm -f myproject_something";
+    };
+    script = ''
+      podman network inspect myproject_something || podman network create myproject_something --label=test-label=okay
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+
+  # Volumes
+  systemd.services."podman-volume-myproject_books" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      podman volume inspect myproject_books || podman volume create myproject_books --opt=device=/mnt/media/Books --opt=o=bind --opt=type=none
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+  systemd.services."podman-volume-photos" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      podman volume inspect photos || podman volume create photos --opt=device=/mnt/photos --opt=o=bind --opt=type=none --label=test-label=okay
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+  systemd.services."podman-volume-storage" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      podman volume inspect storage || podman volume create storage --opt=device=/mnt/media --opt=o=bind --opt=type=none
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+
+  # Root service
+  # When started, this will automatically create all resources and start
+  # the containers. When stopped, this will teardown all resources.
+  systemd.targets."podman-compose-myproject-root" = {
+    unitConfig = {
+      Description = "Root target generated by compose2nix.";
+    };
+  };
+    })
+  ];
+}
diff --git a/testdata/TestEnableOption_WithHeader.docker.nix b/testdata/TestEnableOption_WithHeader.docker.nix
new file mode 100644
index 0000000..4d026e1
--- /dev/null
+++ b/testdata/TestEnableOption_WithHeader.docker.nix
@@ -0,0 +1,358 @@
+# Auto-generated using compose2nix v0.3.2-pre.
+{ pkgs, lib, config, ... }:
+
+{
+  options.myproject = {
+    enable = lib.mkEnableOption "Enable myproject";
+  };
+
+  config = lib.mkMerge [
+    (lib.mkIf config.myproject.enable {
+
+  # Runtime
+  virtualisation.docker = {
+    enable = true;
+    autoPrune.enable = true;
+  };
+  virtualisation.oci-containers.backend = "docker";
+
+  # Containers
+  virtualisation.oci-containers.containers."jellyseerr" = {
+    image = "docker.io/fallenbagel/jellyseerr:latest";
+    environment = {
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/var/volumes/jellyseerr:/app/config:rw"
+      "myproject_books:/books:rw"
+    ];
+    cmd = [ "ls" "-la" "/" ];
+    labels = {
+      "traefik.enable" = "true";
+      "traefik.http.routers.jellyseerr.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.jellyseerr.rule" = "Host(`requests.hello.us`)";
+      "traefik.http.routers.jellyseerr.tls.certresolver" = "htpc";
+    };
+    dependsOn = [
+      "myproject-sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--cpus=1.5"
+      "--dns=1.1.1.1"
+      "--health-cmd=curl -f http://localhost/\${POTATO}"
+      "--memory-reservation=524288000b"
+      "--memory=1048576000b"
+      "--network=container:myproject-sabnzbd"
+    ];
+  };
+  systemd.services."docker-jellyseerr" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "on-failure";
+      RestartMaxDelaySec = lib.mkOverride 90 "1m";
+      RestartSec = lib.mkOverride 90 "100ms";
+      RestartSteps = lib.mkOverride 90 9;
+    };
+    startLimitBurst = 3;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 120;
+    };
+    after = [
+      "docker-volume-myproject_books.service"
+    ];
+    requires = [
+      "docker-volume-myproject_books.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."myproject-sabnzbd" = {
+    image = "lscr.io/linuxserver/sabnzbd";
+    environment = {
+      "DOCKER_MODS" = "ghcr.io/gilbn/theme.park:sabnzbd";
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TP_DOMAIN" = "hey.hello.us\\/themepark";
+      "TP_HOTIO" = "false";
+      "TP_THEME" = "potato";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/var/volumes/sabnzbd:/config:rw"
+      "storage:/storage:rw"
+    ];
+    labels = {
+      "compose2nix.systemd.service.RuntimeMaxSec" = "10";
+      "compose2nix.systemd.unit.Description" = "This is the sabnzbd container!";
+      "traefik.enable" = "true";
+      "traefik.http.routers.sabnzbd.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.sabnzbd.rule" = "Host(`hey.hello.us`) && PathPrefix(`/sabnzbd`)";
+      "traefik.http.routers.sabnzbd.tls.certresolver" = "htpc";
+    };
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--health-cmd=curl -f http://localhost/"
+      "--hostname=sabnzbd"
+      "--network-alias=sabnzbd"
+      "--network=myproject_default"
+    ];
+  };
+  systemd.services."docker-myproject-sabnzbd" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "always";
+      RestartMaxDelaySec = lib.mkOverride 90 "1m";
+      RestartSec = lib.mkOverride 90 "100ms";
+      RestartSteps = lib.mkOverride 90 9;
+      RuntimeMaxSec = lib.mkOverride 90 10;
+    };
+    unitConfig = {
+      Description = lib.mkOverride 90 "This is the sabnzbd container!";
+    };
+    after = [
+      "docker-network-myproject_default.service"
+      "docker-volume-storage.service"
+    ];
+    requires = [
+      "docker-network-myproject_default.service"
+      "docker-volume-storage.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."photoprism-mariadb" = {
+    image = "docker.io/library/mariadb:10.9";
+    environment = {
+      "MARIADB_AUTO_UPGRADE" = "1";
+      "MARIADB_DATABASE" = "photoprism";
+      "MARIADB_INITDB_SKIP_TZINFO" = "1";
+      "MARIADB_PASSWORD" = "insecure";
+      "MARIADB_ROOT_PASSWORD" = "insecure";
+      "MARIADB_USER" = "photoprism";
+    };
+    volumes = [
+      "/var/volumes/photoprism-mariadb:/var/lib/mysql:rw"
+      "photos:/photos:rw"
+    ];
+    user = "1000:1000";
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--health-cmd=[\"curl\", \"-f\", \"http://localhost\"]"
+      "--health-interval=1m30s"
+      "--health-retries=3"
+      "--health-start-interval=5s"
+      "--health-start-period=40s"
+      "--health-timeout=10s"
+      "--network=host"
+    ];
+  };
+  systemd.services."docker-photoprism-mariadb" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "always";
+      RestartMaxDelaySec = lib.mkOverride 90 "1m";
+      RestartSec = lib.mkOverride 90 "100ms";
+      RestartSteps = lib.mkOverride 90 9;
+    };
+    startLimitBurst = 10;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 "infinity";
+    };
+    after = [
+      "docker-volume-photos.service"
+    ];
+    requires = [
+      "docker-volume-photos.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."torrent-client" = {
+    image = "docker.io/haugene/transmission-openvpn";
+    environment = {
+      "GLOBAL_APPLY_PERMISSIONS" = "false";
+      "LOCAL_NETWORK" = "192.168.0.0/16";
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TRANSMISSION_DHT_ENABLED" = "false";
+      "TRANSMISSION_DOWNLOAD_DIR" = "/storage/Downloads/transmission";
+      "TRANSMISSION_HOME" = "/config/transmission-home";
+      "TRANSMISSION_INCOMPLETE_DIR" = "/storage/Downloads/transmission/incomplete";
+      "TRANSMISSION_INCOMPLETE_DIR_ENABLED" = "true";
+      "TRANSMISSION_PEX_ENABLED" = "false";
+      "TRANSMISSION_SCRIPT_TORRENT_DONE_ENABLED" = "true";
+      "TRANSMISSION_SCRIPT_TORRENT_DONE_FILENAME" = "/config/transmission-unpack.sh";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/etc/localtime:/etc/localtime:ro"
+      "/var/volumes/transmission/config:/config:rw"
+      "/var/volumes/transmission/scripts:/scripts:rw"
+      "storage:/storage:rw"
+    ];
+    ports = [
+      "9091:9091/tcp"
+    ];
+    labels = {
+      "autoheal" = "true";
+      "compose2nix.settings.autoStart" = "false";
+      "traefik.enable" = "true";
+      "traefik.http.routers.transmission.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.transmission.rule" = "Host(`hey.hello.us`) && PathPrefix(`/transmission`)";
+      "traefik.http.routers.transmission.tls.certresolver" = "htpc";
+      "traefik.http.services.transmission.loadbalancer.server.port" = "9091";
+    };
+    dependsOn = [
+      "myproject-sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--add-host=abc:93.184.216.34"
+      "--add-host=abc:::1"
+      "--cap-add=NET_ADMIN"
+      "--device=/dev/net/tun:/dev/net/tun:rwm"
+      "--dns=8.8.4.4"
+      "--dns=8.8.8.8"
+      "--network-alias=my-torrent-client"
+      "--network-alias=transmission"
+      "--network=myproject_something"
+      "--no-healthcheck"
+      "--privileged"
+      "--shm-size=67108864"
+      "--sysctl=net.ipv6.conf.all.disable_ipv6=0"
+    ];
+  };
+  systemd.services."docker-torrent-client" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "on-failure";
+    };
+    startLimitBurst = 3;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 "infinity";
+    };
+    after = [
+      "docker-network-myproject_something.service"
+      "docker-volume-storage.service"
+    ];
+    requires = [
+      "docker-network-myproject_something.service"
+      "docker-volume-storage.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."traefik" = {
+    image = "docker.io/library/traefik";
+    environment = {
+      "CLOUDFLARE_API_KEY" = "yomama";
+      "CLOUDFLARE_EMAIL" = "aaa@aaa.com";
+    };
+    volumes = [
+      "/var/run/podman/podman.sock:/var/run/docker.sock:ro"
+      "/var/volumes/traefik:/etc/traefik:rw"
+    ];
+    ports = [
+      "80:80/tcp"
+      "443:443/tcp"
+    ];
+    labels = {
+      "compose2nix.systemd.service.Restart" = "'no'";
+      "compose2nix.systemd.unit.AllowIsolate" = "true";
+      "traefik.enable" = "true";
+      "traefik.http.routers.traefik.entrypoints" = "https";
+      "traefik.http.routers.traefik.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.traefik.rule" = "Host(`hey.hello.us`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))";
+      "traefik.http.routers.traefik.service" = "api@internal";
+      "traefik.http.routers.traefik.tls.certresolver" = "htpc";
+    };
+    dependsOn = [
+      "sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--network=container:sabnzbd"
+    ];
+  };
+  systemd.services."docker-traefik" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "no";
+    };
+    unitConfig = {
+      AllowIsolate = lib.mkOverride 90 true;
+    };
+  };
+
+  # Networks
+  systemd.services."docker-network-myproject_default" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStop = "docker network rm -f myproject_default";
+    };
+    script = ''
+      docker network inspect myproject_default || docker network create myproject_default
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+  systemd.services."docker-network-myproject_something" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStop = "docker network rm -f myproject_something";
+    };
+    script = ''
+      docker network inspect myproject_something || docker network create myproject_something --label=test-label=okay
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+
+  # Volumes
+  systemd.services."docker-volume-myproject_books" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      docker volume inspect myproject_books || docker volume create myproject_books --opt=device=/mnt/media/Books --opt=o=bind --opt=type=none
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+  systemd.services."docker-volume-photos" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      docker volume inspect photos || docker volume create photos --opt=device=/mnt/photos --opt=o=bind --opt=type=none --label=test-label=okay
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+  systemd.services."docker-volume-storage" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      docker volume inspect storage || docker volume create storage --opt=device=/mnt/media --opt=o=bind --opt=type=none
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+
+  # Root service
+  # When started, this will automatically create all resources and start
+  # the containers. When stopped, this will teardown all resources.
+  systemd.targets."docker-compose-myproject-root" = {
+    unitConfig = {
+      Description = "Root target generated by compose2nix.";
+    };
+  };
+    })
+  ];
+}
diff --git a/testdata/TestEnableOption_WithHeader.podman.nix b/testdata/TestEnableOption_WithHeader.podman.nix
new file mode 100644
index 0000000..5cef229
--- /dev/null
+++ b/testdata/TestEnableOption_WithHeader.podman.nix
@@ -0,0 +1,359 @@
+# Auto-generated using compose2nix v0.3.2-pre.
+{ pkgs, lib, config, ... }:
+
+{
+  options.myproject = {
+    enable = lib.mkEnableOption "Enable myproject";
+  };
+
+  config = lib.mkMerge [
+    (lib.mkIf config.myproject.enable {
+
+  # Runtime
+  virtualisation.podman = {
+    enable = true;
+    autoPrune.enable = true;
+    dockerCompat = true;
+  };
+
+  # Enable container name DNS for all Podman networks.
+  networking.firewall.interfaces = let
+    matchAll = if !config.networking.nftables.enable then "podman+" else "podman*";
+  in {
+    "${matchAll}".allowedUDPPorts = [ 53 ];
+  };
+
+  virtualisation.oci-containers.backend = "podman";
+
+  # Containers
+  virtualisation.oci-containers.containers."jellyseerr" = {
+    image = "docker.io/fallenbagel/jellyseerr:latest";
+    environment = {
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/var/volumes/jellyseerr:/app/config:rw"
+      "myproject_books:/books:rw"
+    ];
+    cmd = [ "ls" "-la" "/" ];
+    labels = {
+      "traefik.enable" = "true";
+      "traefik.http.routers.jellyseerr.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.jellyseerr.rule" = "Host(`requests.hello.us`)";
+      "traefik.http.routers.jellyseerr.tls.certresolver" = "htpc";
+    };
+    dependsOn = [
+      "myproject-sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--cpus=1.5"
+      "--dns=1.1.1.1"
+      "--health-cmd=curl -f http://localhost/\${POTATO}"
+      "--memory-reservation=524288000b"
+      "--memory=1048576000b"
+      "--network=container:myproject-sabnzbd"
+    ];
+  };
+  systemd.services."podman-jellyseerr" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "on-failure";
+      RestartSec = lib.mkOverride 90 "5s";
+    };
+    startLimitBurst = 3;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 120;
+    };
+    after = [
+      "podman-volume-myproject_books.service"
+    ];
+    requires = [
+      "podman-volume-myproject_books.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."myproject-sabnzbd" = {
+    image = "lscr.io/linuxserver/sabnzbd";
+    environment = {
+      "DOCKER_MODS" = "ghcr.io/gilbn/theme.park:sabnzbd";
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TP_DOMAIN" = "hey.hello.us\\/themepark";
+      "TP_HOTIO" = "false";
+      "TP_THEME" = "potato";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/var/volumes/sabnzbd:/config:rw"
+      "storage:/storage:rw"
+    ];
+    labels = {
+      "compose2nix.systemd.service.RuntimeMaxSec" = "10";
+      "compose2nix.systemd.unit.Description" = "This is the sabnzbd container!";
+      "traefik.enable" = "true";
+      "traefik.http.routers.sabnzbd.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.sabnzbd.rule" = "Host(`hey.hello.us`) && PathPrefix(`/sabnzbd`)";
+      "traefik.http.routers.sabnzbd.tls.certresolver" = "htpc";
+    };
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--health-cmd=curl -f http://localhost/"
+      "--hostname=sabnzbd"
+      "--network-alias=sabnzbd"
+      "--network=myproject_default"
+    ];
+  };
+  systemd.services."podman-myproject-sabnzbd" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "always";
+      RuntimeMaxSec = lib.mkOverride 90 10;
+    };
+    unitConfig = {
+      Description = lib.mkOverride 90 "This is the sabnzbd container!";
+    };
+    after = [
+      "podman-network-myproject_default.service"
+      "podman-volume-storage.service"
+    ];
+    requires = [
+      "podman-network-myproject_default.service"
+      "podman-volume-storage.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."photoprism-mariadb" = {
+    image = "docker.io/library/mariadb:10.9";
+    environment = {
+      "MARIADB_AUTO_UPGRADE" = "1";
+      "MARIADB_DATABASE" = "photoprism";
+      "MARIADB_INITDB_SKIP_TZINFO" = "1";
+      "MARIADB_PASSWORD" = "insecure";
+      "MARIADB_ROOT_PASSWORD" = "insecure";
+      "MARIADB_USER" = "photoprism";
+    };
+    volumes = [
+      "/var/volumes/photoprism-mariadb:/var/lib/mysql:rw"
+      "photos:/photos:rw"
+    ];
+    user = "1000:1000";
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--health-cmd=[\"curl\", \"-f\", \"http://localhost\"]"
+      "--health-interval=1m30s"
+      "--health-retries=3"
+      "--health-start-period=40s"
+      "--health-startup-interval=5s"
+      "--health-timeout=10s"
+      "--network=host"
+    ];
+  };
+  systemd.services."podman-photoprism-mariadb" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "always";
+      RestartSec = lib.mkOverride 90 "3m0s";
+    };
+    startLimitBurst = 10;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 "infinity";
+    };
+    after = [
+      "podman-volume-photos.service"
+    ];
+    requires = [
+      "podman-volume-photos.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."torrent-client" = {
+    image = "docker.io/haugene/transmission-openvpn";
+    environment = {
+      "GLOBAL_APPLY_PERMISSIONS" = "false";
+      "LOCAL_NETWORK" = "192.168.0.0/16";
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TRANSMISSION_DHT_ENABLED" = "false";
+      "TRANSMISSION_DOWNLOAD_DIR" = "/storage/Downloads/transmission";
+      "TRANSMISSION_HOME" = "/config/transmission-home";
+      "TRANSMISSION_INCOMPLETE_DIR" = "/storage/Downloads/transmission/incomplete";
+      "TRANSMISSION_INCOMPLETE_DIR_ENABLED" = "true";
+      "TRANSMISSION_PEX_ENABLED" = "false";
+      "TRANSMISSION_SCRIPT_TORRENT_DONE_ENABLED" = "true";
+      "TRANSMISSION_SCRIPT_TORRENT_DONE_FILENAME" = "/config/transmission-unpack.sh";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/etc/localtime:/etc/localtime:ro"
+      "/var/volumes/transmission/config:/config:rw"
+      "/var/volumes/transmission/scripts:/scripts:rw"
+      "storage:/storage:rw"
+    ];
+    ports = [
+      "9091:9091/tcp"
+    ];
+    labels = {
+      "autoheal" = "true";
+      "compose2nix.settings.autoStart" = "false";
+      "traefik.enable" = "true";
+      "traefik.http.routers.transmission.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.transmission.rule" = "Host(`hey.hello.us`) && PathPrefix(`/transmission`)";
+      "traefik.http.routers.transmission.tls.certresolver" = "htpc";
+      "traefik.http.services.transmission.loadbalancer.server.port" = "9091";
+    };
+    dependsOn = [
+      "myproject-sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--add-host=abc:93.184.216.34"
+      "--add-host=abc:::1"
+      "--cap-add=NET_ADMIN"
+      "--device=/dev/net/tun:/dev/net/tun:rwm"
+      "--dns=8.8.4.4"
+      "--dns=8.8.8.8"
+      "--network-alias=transmission"
+      "--network=myproject_something:alias=my-torrent-client"
+      "--no-healthcheck"
+      "--privileged"
+      "--shm-size=67108864"
+      "--sysctl=net.ipv6.conf.all.disable_ipv6=0"
+    ];
+  };
+  systemd.services."podman-torrent-client" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "on-failure";
+    };
+    startLimitBurst = 3;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 "infinity";
+    };
+    after = [
+      "podman-network-myproject_something.service"
+      "podman-volume-storage.service"
+    ];
+    requires = [
+      "podman-network-myproject_something.service"
+      "podman-volume-storage.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."traefik" = {
+    image = "docker.io/library/traefik";
+    environment = {
+      "CLOUDFLARE_API_KEY" = "yomama";
+      "CLOUDFLARE_EMAIL" = "aaa@aaa.com";
+    };
+    volumes = [
+      "/var/run/podman/podman.sock:/var/run/docker.sock:ro"
+      "/var/volumes/traefik:/etc/traefik:rw"
+    ];
+    ports = [
+      "80:80/tcp"
+      "443:443/tcp"
+    ];
+    labels = {
+      "compose2nix.systemd.service.Restart" = "'no'";
+      "compose2nix.systemd.unit.AllowIsolate" = "true";
+      "traefik.enable" = "true";
+      "traefik.http.routers.traefik.entrypoints" = "https";
+      "traefik.http.routers.traefik.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.traefik.rule" = "Host(`hey.hello.us`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))";
+      "traefik.http.routers.traefik.service" = "api@internal";
+      "traefik.http.routers.traefik.tls.certresolver" = "htpc";
+    };
+    dependsOn = [
+      "sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--network=container:sabnzbd"
+    ];
+  };
+  systemd.services."podman-traefik" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "no";
+    };
+    unitConfig = {
+      AllowIsolate = lib.mkOverride 90 true;
+    };
+  };
+
+  # Networks
+  systemd.services."podman-network-myproject_default" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStop = "podman network rm -f myproject_default";
+    };
+    script = ''
+      podman network inspect myproject_default || podman network create myproject_default
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+  systemd.services."podman-network-myproject_something" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStop = "podman network rm -f myproject_something";
+    };
+    script = ''
+      podman network inspect myproject_something || podman network create myproject_something --label=test-label=okay
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+
+  # Volumes
+  systemd.services."podman-volume-myproject_books" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      podman volume inspect myproject_books || podman volume create myproject_books --opt=device=/mnt/media/Books --opt=o=bind --opt=type=none
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+  systemd.services."podman-volume-photos" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      podman volume inspect photos || podman volume create photos --opt=device=/mnt/photos --opt=o=bind --opt=type=none --label=test-label=okay
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+  systemd.services."podman-volume-storage" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      podman volume inspect storage || podman volume create storage --opt=device=/mnt/media --opt=o=bind --opt=type=none
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+
+  # Root service
+  # When started, this will automatically create all resources and start
+  # the containers. When stopped, this will teardown all resources.
+  systemd.targets."podman-compose-myproject-root" = {
+    unitConfig = {
+      Description = "Root target generated by compose2nix.";
+    };
+  };
+    })
+  ];
+}
diff --git a/testdata/TestOptionPrefix.docker.nix b/testdata/TestOptionPrefix.docker.nix
new file mode 100644
index 0000000..9e918c6
--- /dev/null
+++ b/testdata/TestOptionPrefix.docker.nix
@@ -0,0 +1,357 @@
+{ pkgs, lib, config, ... }:
+
+{
+  options.custom.containers.myproject = {
+    enable = lib.mkEnableOption "Enable myproject";
+  };
+
+  config = lib.mkMerge [
+    (lib.mkIf config.custom.containers.myproject.enable {
+
+  # Runtime
+  virtualisation.docker = {
+    enable = true;
+    autoPrune.enable = true;
+  };
+  virtualisation.oci-containers.backend = "docker";
+
+  # Containers
+  virtualisation.oci-containers.containers."jellyseerr" = {
+    image = "docker.io/fallenbagel/jellyseerr:latest";
+    environment = {
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/var/volumes/jellyseerr:/app/config:rw"
+      "myproject_books:/books:rw"
+    ];
+    cmd = [ "ls" "-la" "/" ];
+    labels = {
+      "traefik.enable" = "true";
+      "traefik.http.routers.jellyseerr.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.jellyseerr.rule" = "Host(`requests.hello.us`)";
+      "traefik.http.routers.jellyseerr.tls.certresolver" = "htpc";
+    };
+    dependsOn = [
+      "myproject-sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--cpus=1.5"
+      "--dns=1.1.1.1"
+      "--health-cmd=curl -f http://localhost/\${POTATO}"
+      "--memory-reservation=524288000b"
+      "--memory=1048576000b"
+      "--network=container:myproject-sabnzbd"
+    ];
+  };
+  systemd.services."docker-jellyseerr" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "on-failure";
+      RestartMaxDelaySec = lib.mkOverride 90 "1m";
+      RestartSec = lib.mkOverride 90 "100ms";
+      RestartSteps = lib.mkOverride 90 9;
+    };
+    startLimitBurst = 3;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 120;
+    };
+    after = [
+      "docker-volume-myproject_books.service"
+    ];
+    requires = [
+      "docker-volume-myproject_books.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."myproject-sabnzbd" = {
+    image = "lscr.io/linuxserver/sabnzbd";
+    environment = {
+      "DOCKER_MODS" = "ghcr.io/gilbn/theme.park:sabnzbd";
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TP_DOMAIN" = "hey.hello.us\\/themepark";
+      "TP_HOTIO" = "false";
+      "TP_THEME" = "potato";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/var/volumes/sabnzbd:/config:rw"
+      "storage:/storage:rw"
+    ];
+    labels = {
+      "compose2nix.systemd.service.RuntimeMaxSec" = "10";
+      "compose2nix.systemd.unit.Description" = "This is the sabnzbd container!";
+      "traefik.enable" = "true";
+      "traefik.http.routers.sabnzbd.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.sabnzbd.rule" = "Host(`hey.hello.us`) && PathPrefix(`/sabnzbd`)";
+      "traefik.http.routers.sabnzbd.tls.certresolver" = "htpc";
+    };
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--health-cmd=curl -f http://localhost/"
+      "--hostname=sabnzbd"
+      "--network-alias=sabnzbd"
+      "--network=myproject_default"
+    ];
+  };
+  systemd.services."docker-myproject-sabnzbd" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "always";
+      RestartMaxDelaySec = lib.mkOverride 90 "1m";
+      RestartSec = lib.mkOverride 90 "100ms";
+      RestartSteps = lib.mkOverride 90 9;
+      RuntimeMaxSec = lib.mkOverride 90 10;
+    };
+    unitConfig = {
+      Description = lib.mkOverride 90 "This is the sabnzbd container!";
+    };
+    after = [
+      "docker-network-myproject_default.service"
+      "docker-volume-storage.service"
+    ];
+    requires = [
+      "docker-network-myproject_default.service"
+      "docker-volume-storage.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."photoprism-mariadb" = {
+    image = "docker.io/library/mariadb:10.9";
+    environment = {
+      "MARIADB_AUTO_UPGRADE" = "1";
+      "MARIADB_DATABASE" = "photoprism";
+      "MARIADB_INITDB_SKIP_TZINFO" = "1";
+      "MARIADB_PASSWORD" = "insecure";
+      "MARIADB_ROOT_PASSWORD" = "insecure";
+      "MARIADB_USER" = "photoprism";
+    };
+    volumes = [
+      "/var/volumes/photoprism-mariadb:/var/lib/mysql:rw"
+      "photos:/photos:rw"
+    ];
+    user = "1000:1000";
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--health-cmd=[\"curl\", \"-f\", \"http://localhost\"]"
+      "--health-interval=1m30s"
+      "--health-retries=3"
+      "--health-start-interval=5s"
+      "--health-start-period=40s"
+      "--health-timeout=10s"
+      "--network=host"
+    ];
+  };
+  systemd.services."docker-photoprism-mariadb" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "always";
+      RestartMaxDelaySec = lib.mkOverride 90 "1m";
+      RestartSec = lib.mkOverride 90 "100ms";
+      RestartSteps = lib.mkOverride 90 9;
+    };
+    startLimitBurst = 10;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 "infinity";
+    };
+    after = [
+      "docker-volume-photos.service"
+    ];
+    requires = [
+      "docker-volume-photos.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."torrent-client" = {
+    image = "docker.io/haugene/transmission-openvpn";
+    environment = {
+      "GLOBAL_APPLY_PERMISSIONS" = "false";
+      "LOCAL_NETWORK" = "192.168.0.0/16";
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TRANSMISSION_DHT_ENABLED" = "false";
+      "TRANSMISSION_DOWNLOAD_DIR" = "/storage/Downloads/transmission";
+      "TRANSMISSION_HOME" = "/config/transmission-home";
+      "TRANSMISSION_INCOMPLETE_DIR" = "/storage/Downloads/transmission/incomplete";
+      "TRANSMISSION_INCOMPLETE_DIR_ENABLED" = "true";
+      "TRANSMISSION_PEX_ENABLED" = "false";
+      "TRANSMISSION_SCRIPT_TORRENT_DONE_ENABLED" = "true";
+      "TRANSMISSION_SCRIPT_TORRENT_DONE_FILENAME" = "/config/transmission-unpack.sh";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/etc/localtime:/etc/localtime:ro"
+      "/var/volumes/transmission/config:/config:rw"
+      "/var/volumes/transmission/scripts:/scripts:rw"
+      "storage:/storage:rw"
+    ];
+    ports = [
+      "9091:9091/tcp"
+    ];
+    labels = {
+      "autoheal" = "true";
+      "compose2nix.settings.autoStart" = "false";
+      "traefik.enable" = "true";
+      "traefik.http.routers.transmission.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.transmission.rule" = "Host(`hey.hello.us`) && PathPrefix(`/transmission`)";
+      "traefik.http.routers.transmission.tls.certresolver" = "htpc";
+      "traefik.http.services.transmission.loadbalancer.server.port" = "9091";
+    };
+    dependsOn = [
+      "myproject-sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--add-host=abc:93.184.216.34"
+      "--add-host=abc:::1"
+      "--cap-add=NET_ADMIN"
+      "--device=/dev/net/tun:/dev/net/tun:rwm"
+      "--dns=8.8.4.4"
+      "--dns=8.8.8.8"
+      "--network-alias=my-torrent-client"
+      "--network-alias=transmission"
+      "--network=myproject_something"
+      "--no-healthcheck"
+      "--privileged"
+      "--shm-size=67108864"
+      "--sysctl=net.ipv6.conf.all.disable_ipv6=0"
+    ];
+  };
+  systemd.services."docker-torrent-client" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "on-failure";
+    };
+    startLimitBurst = 3;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 "infinity";
+    };
+    after = [
+      "docker-network-myproject_something.service"
+      "docker-volume-storage.service"
+    ];
+    requires = [
+      "docker-network-myproject_something.service"
+      "docker-volume-storage.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."traefik" = {
+    image = "docker.io/library/traefik";
+    environment = {
+      "CLOUDFLARE_API_KEY" = "yomama";
+      "CLOUDFLARE_EMAIL" = "aaa@aaa.com";
+    };
+    volumes = [
+      "/var/run/podman/podman.sock:/var/run/docker.sock:ro"
+      "/var/volumes/traefik:/etc/traefik:rw"
+    ];
+    ports = [
+      "80:80/tcp"
+      "443:443/tcp"
+    ];
+    labels = {
+      "compose2nix.systemd.service.Restart" = "'no'";
+      "compose2nix.systemd.unit.AllowIsolate" = "true";
+      "traefik.enable" = "true";
+      "traefik.http.routers.traefik.entrypoints" = "https";
+      "traefik.http.routers.traefik.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.traefik.rule" = "Host(`hey.hello.us`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))";
+      "traefik.http.routers.traefik.service" = "api@internal";
+      "traefik.http.routers.traefik.tls.certresolver" = "htpc";
+    };
+    dependsOn = [
+      "sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--network=container:sabnzbd"
+    ];
+  };
+  systemd.services."docker-traefik" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "no";
+    };
+    unitConfig = {
+      AllowIsolate = lib.mkOverride 90 true;
+    };
+  };
+
+  # Networks
+  systemd.services."docker-network-myproject_default" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStop = "docker network rm -f myproject_default";
+    };
+    script = ''
+      docker network inspect myproject_default || docker network create myproject_default
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+  systemd.services."docker-network-myproject_something" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStop = "docker network rm -f myproject_something";
+    };
+    script = ''
+      docker network inspect myproject_something || docker network create myproject_something --label=test-label=okay
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+
+  # Volumes
+  systemd.services."docker-volume-myproject_books" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      docker volume inspect myproject_books || docker volume create myproject_books --opt=device=/mnt/media/Books --opt=o=bind --opt=type=none
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+  systemd.services."docker-volume-photos" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      docker volume inspect photos || docker volume create photos --opt=device=/mnt/photos --opt=o=bind --opt=type=none --label=test-label=okay
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+  systemd.services."docker-volume-storage" = {
+    path = [ pkgs.docker ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      docker volume inspect storage || docker volume create storage --opt=device=/mnt/media --opt=o=bind --opt=type=none
+    '';
+    partOf = [ "docker-compose-myproject-root.target" ];
+    wantedBy = [ "docker-compose-myproject-root.target" ];
+  };
+
+  # Root service
+  # When started, this will automatically create all resources and start
+  # the containers. When stopped, this will teardown all resources.
+  systemd.targets."docker-compose-myproject-root" = {
+    unitConfig = {
+      Description = "Root target generated by compose2nix.";
+    };
+  };
+    })
+  ];
+}
diff --git a/testdata/TestOptionPrefix.podman.nix b/testdata/TestOptionPrefix.podman.nix
new file mode 100644
index 0000000..6f384d1
--- /dev/null
+++ b/testdata/TestOptionPrefix.podman.nix
@@ -0,0 +1,358 @@
+{ pkgs, lib, config, ... }:
+
+{
+  options.custom.containers.myproject = {
+    enable = lib.mkEnableOption "Enable myproject";
+  };
+
+  config = lib.mkMerge [
+    (lib.mkIf config.custom.containers.myproject.enable {
+
+  # Runtime
+  virtualisation.podman = {
+    enable = true;
+    autoPrune.enable = true;
+    dockerCompat = true;
+  };
+
+  # Enable container name DNS for all Podman networks.
+  networking.firewall.interfaces = let
+    matchAll = if !config.networking.nftables.enable then "podman+" else "podman*";
+  in {
+    "${matchAll}".allowedUDPPorts = [ 53 ];
+  };
+
+  virtualisation.oci-containers.backend = "podman";
+
+  # Containers
+  virtualisation.oci-containers.containers."jellyseerr" = {
+    image = "docker.io/fallenbagel/jellyseerr:latest";
+    environment = {
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/var/volumes/jellyseerr:/app/config:rw"
+      "myproject_books:/books:rw"
+    ];
+    cmd = [ "ls" "-la" "/" ];
+    labels = {
+      "traefik.enable" = "true";
+      "traefik.http.routers.jellyseerr.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.jellyseerr.rule" = "Host(`requests.hello.us`)";
+      "traefik.http.routers.jellyseerr.tls.certresolver" = "htpc";
+    };
+    dependsOn = [
+      "myproject-sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--cpus=1.5"
+      "--dns=1.1.1.1"
+      "--health-cmd=curl -f http://localhost/\${POTATO}"
+      "--memory-reservation=524288000b"
+      "--memory=1048576000b"
+      "--network=container:myproject-sabnzbd"
+    ];
+  };
+  systemd.services."podman-jellyseerr" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "on-failure";
+      RestartSec = lib.mkOverride 90 "5s";
+    };
+    startLimitBurst = 3;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 120;
+    };
+    after = [
+      "podman-volume-myproject_books.service"
+    ];
+    requires = [
+      "podman-volume-myproject_books.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."myproject-sabnzbd" = {
+    image = "lscr.io/linuxserver/sabnzbd";
+    environment = {
+      "DOCKER_MODS" = "ghcr.io/gilbn/theme.park:sabnzbd";
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TP_DOMAIN" = "hey.hello.us\\/themepark";
+      "TP_HOTIO" = "false";
+      "TP_THEME" = "potato";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/var/volumes/sabnzbd:/config:rw"
+      "storage:/storage:rw"
+    ];
+    labels = {
+      "compose2nix.systemd.service.RuntimeMaxSec" = "10";
+      "compose2nix.systemd.unit.Description" = "This is the sabnzbd container!";
+      "traefik.enable" = "true";
+      "traefik.http.routers.sabnzbd.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.sabnzbd.rule" = "Host(`hey.hello.us`) && PathPrefix(`/sabnzbd`)";
+      "traefik.http.routers.sabnzbd.tls.certresolver" = "htpc";
+    };
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--health-cmd=curl -f http://localhost/"
+      "--hostname=sabnzbd"
+      "--network-alias=sabnzbd"
+      "--network=myproject_default"
+    ];
+  };
+  systemd.services."podman-myproject-sabnzbd" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "always";
+      RuntimeMaxSec = lib.mkOverride 90 10;
+    };
+    unitConfig = {
+      Description = lib.mkOverride 90 "This is the sabnzbd container!";
+    };
+    after = [
+      "podman-network-myproject_default.service"
+      "podman-volume-storage.service"
+    ];
+    requires = [
+      "podman-network-myproject_default.service"
+      "podman-volume-storage.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."photoprism-mariadb" = {
+    image = "docker.io/library/mariadb:10.9";
+    environment = {
+      "MARIADB_AUTO_UPGRADE" = "1";
+      "MARIADB_DATABASE" = "photoprism";
+      "MARIADB_INITDB_SKIP_TZINFO" = "1";
+      "MARIADB_PASSWORD" = "insecure";
+      "MARIADB_ROOT_PASSWORD" = "insecure";
+      "MARIADB_USER" = "photoprism";
+    };
+    volumes = [
+      "/var/volumes/photoprism-mariadb:/var/lib/mysql:rw"
+      "photos:/photos:rw"
+    ];
+    user = "1000:1000";
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--health-cmd=[\"curl\", \"-f\", \"http://localhost\"]"
+      "--health-interval=1m30s"
+      "--health-retries=3"
+      "--health-start-period=40s"
+      "--health-startup-interval=5s"
+      "--health-timeout=10s"
+      "--network=host"
+    ];
+  };
+  systemd.services."podman-photoprism-mariadb" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "always";
+      RestartSec = lib.mkOverride 90 "3m0s";
+    };
+    startLimitBurst = 10;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 "infinity";
+    };
+    after = [
+      "podman-volume-photos.service"
+    ];
+    requires = [
+      "podman-volume-photos.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."torrent-client" = {
+    image = "docker.io/haugene/transmission-openvpn";
+    environment = {
+      "GLOBAL_APPLY_PERMISSIONS" = "false";
+      "LOCAL_NETWORK" = "192.168.0.0/16";
+      "PGID" = "1000";
+      "PUID" = "1000";
+      "TRANSMISSION_DHT_ENABLED" = "false";
+      "TRANSMISSION_DOWNLOAD_DIR" = "/storage/Downloads/transmission";
+      "TRANSMISSION_HOME" = "/config/transmission-home";
+      "TRANSMISSION_INCOMPLETE_DIR" = "/storage/Downloads/transmission/incomplete";
+      "TRANSMISSION_INCOMPLETE_DIR_ENABLED" = "true";
+      "TRANSMISSION_PEX_ENABLED" = "false";
+      "TRANSMISSION_SCRIPT_TORRENT_DONE_ENABLED" = "true";
+      "TRANSMISSION_SCRIPT_TORRENT_DONE_FILENAME" = "/config/transmission-unpack.sh";
+      "TZ" = "America/New_York";
+    };
+    volumes = [
+      "/etc/localtime:/etc/localtime:ro"
+      "/var/volumes/transmission/config:/config:rw"
+      "/var/volumes/transmission/scripts:/scripts:rw"
+      "storage:/storage:rw"
+    ];
+    ports = [
+      "9091:9091/tcp"
+    ];
+    labels = {
+      "autoheal" = "true";
+      "compose2nix.settings.autoStart" = "false";
+      "traefik.enable" = "true";
+      "traefik.http.routers.transmission.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.transmission.rule" = "Host(`hey.hello.us`) && PathPrefix(`/transmission`)";
+      "traefik.http.routers.transmission.tls.certresolver" = "htpc";
+      "traefik.http.services.transmission.loadbalancer.server.port" = "9091";
+    };
+    dependsOn = [
+      "myproject-sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--add-host=abc:93.184.216.34"
+      "--add-host=abc:::1"
+      "--cap-add=NET_ADMIN"
+      "--device=/dev/net/tun:/dev/net/tun:rwm"
+      "--dns=8.8.4.4"
+      "--dns=8.8.8.8"
+      "--network-alias=transmission"
+      "--network=myproject_something:alias=my-torrent-client"
+      "--no-healthcheck"
+      "--privileged"
+      "--shm-size=67108864"
+      "--sysctl=net.ipv6.conf.all.disable_ipv6=0"
+    ];
+  };
+  systemd.services."podman-torrent-client" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "on-failure";
+    };
+    startLimitBurst = 3;
+    unitConfig = {
+      StartLimitIntervalSec = lib.mkOverride 90 "infinity";
+    };
+    after = [
+      "podman-network-myproject_something.service"
+      "podman-volume-storage.service"
+    ];
+    requires = [
+      "podman-network-myproject_something.service"
+      "podman-volume-storage.service"
+    ];
+  };
+  virtualisation.oci-containers.containers."traefik" = {
+    image = "docker.io/library/traefik";
+    environment = {
+      "CLOUDFLARE_API_KEY" = "yomama";
+      "CLOUDFLARE_EMAIL" = "aaa@aaa.com";
+    };
+    volumes = [
+      "/var/run/podman/podman.sock:/var/run/docker.sock:ro"
+      "/var/volumes/traefik:/etc/traefik:rw"
+    ];
+    ports = [
+      "80:80/tcp"
+      "443:443/tcp"
+    ];
+    labels = {
+      "compose2nix.systemd.service.Restart" = "'no'";
+      "compose2nix.systemd.unit.AllowIsolate" = "true";
+      "traefik.enable" = "true";
+      "traefik.http.routers.traefik.entrypoints" = "https";
+      "traefik.http.routers.traefik.middlewares" = "chain-authelia@file";
+      "traefik.http.routers.traefik.rule" = "Host(`hey.hello.us`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))";
+      "traefik.http.routers.traefik.service" = "api@internal";
+      "traefik.http.routers.traefik.tls.certresolver" = "htpc";
+    };
+    dependsOn = [
+      "sabnzbd"
+    ];
+    log-driver = "journald";
+    autoStart = false;
+    extraOptions = [
+      "--network=container:sabnzbd"
+    ];
+  };
+  systemd.services."podman-traefik" = {
+    serviceConfig = {
+      Restart = lib.mkOverride 90 "no";
+    };
+    unitConfig = {
+      AllowIsolate = lib.mkOverride 90 true;
+    };
+  };
+
+  # Networks
+  systemd.services."podman-network-myproject_default" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStop = "podman network rm -f myproject_default";
+    };
+    script = ''
+      podman network inspect myproject_default || podman network create myproject_default
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+  systemd.services."podman-network-myproject_something" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStop = "podman network rm -f myproject_something";
+    };
+    script = ''
+      podman network inspect myproject_something || podman network create myproject_something --label=test-label=okay
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+
+  # Volumes
+  systemd.services."podman-volume-myproject_books" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      podman volume inspect myproject_books || podman volume create myproject_books --opt=device=/mnt/media/Books --opt=o=bind --opt=type=none
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+  systemd.services."podman-volume-photos" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      podman volume inspect photos || podman volume create photos --opt=device=/mnt/photos --opt=o=bind --opt=type=none --label=test-label=okay
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+  systemd.services."podman-volume-storage" = {
+    path = [ pkgs.podman ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+    script = ''
+      podman volume inspect storage || podman volume create storage --opt=device=/mnt/media --opt=o=bind --opt=type=none
+    '';
+    partOf = [ "podman-compose-myproject-root.target" ];
+    wantedBy = [ "podman-compose-myproject-root.target" ];
+  };
+
+  # Root service
+  # When started, this will automatically create all resources and start
+  # the containers. When stopped, this will teardown all resources.
+  systemd.targets."podman-compose-myproject-root" = {
+    unitConfig = {
+      Description = "Root target generated by compose2nix.";
+    };
+  };
+    })
+  ];
+}