diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,23 @@
 # Revision history for Arion
 
-## Next 0.2.0.0
+## 0.2.1.0 -- 2023-07-26
 
+### Added
+
+* `service.networks` now supports attribute set values with various options, thanks to @pedorich-n.
+* `docker-compose.volumes` can now be specified in multiple modules, thanks to @qaifshaikh.
+* `image.fakeRootCommands` for making modifications to the image that aren't "add a link farm".
+
+### Fixed
+
+* Regular maintenance fixes, including one by olebedev
+
+
+## 0.2.0.0 -- 2022-12-02
+
 ### BREAKING
 
 * The `project.name` option is now mandatory for projects that aren't deployed with the NixOS module.
-
 
 * The NixOS module now sets the default network name to the project name (commonly referred to as `<name>` in the option path).
   If this is not desired, for instance if you need the projects to be on the same network, set `networks.default.name` in each of them.
diff --git a/arion-compose.cabal b/arion-compose.cabal
--- a/arion-compose.cabal
+++ b/arion-compose.cabal
@@ -1,7 +1,7 @@
 cabal-version:       2.4
 
 name:                arion-compose
-version: 0.2.0.0
+version: 0.2.1.0
 synopsis:            Run docker-compose with help from Nix/NixOS
 description:         Arion is a tool for building and running applications that consist of multiple docker containers using NixOS modules. It has special support for docker images that are built with Nix, for a smooth development experience and improved performance.
 homepage:            https://github.com/hercules-ci/arion#readme
@@ -30,7 +30,7 @@
   location: https://github.com/hercules-ci/arion
 
 common common
-  build-depends:     base >=4.12.0.0 && <4.17
+  build-depends:     base >=4.12.0.0 && <4.99
                    , aeson >=2
                    , aeson-pretty
                    , async
diff --git a/src/haskell/lib/Arion/Services.hs b/src/haskell/lib/Arion/Services.hs
--- a/src/haskell/lib/Arion/Services.hs
+++ b/src/haskell/lib/Arion/Services.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE CPP #-}
 module Arion.Services
   ( getDefaultExec
   ) where
@@ -9,16 +10,28 @@
 import Protolude hiding (to)
 
 import qualified Data.Aeson as Aeson
+#if MIN_VERSION_lens_aeson(1,2,0)
 import qualified Data.Aeson.Key as AK
+#endif
 import           Arion.Aeson (decodeFile)
 
 import Control.Lens
 import Data.Aeson.Lens
 
+#if MIN_VERSION_lens_aeson(1,2,0)
+type Key = AK.Key
+mkKey :: Text -> Key
+mkKey = AK.fromText
+#else
+type Key = Text
+mkKey :: Text -> Key
+mkKey = identity
+#endif
+
 -- | Subject to change
 getDefaultExec :: FilePath -> Text -> IO [Text]
 getDefaultExec fp service = do
 
   v <- decodeFile fp
 
-  pure ((v :: Aeson.Value) ^.. key "x-arion" . key "serviceInfo" . key (AK.fromText service) . key "defaultExec" . _Array . traverse . _String)
+  pure ((v :: Aeson.Value) ^.. key "x-arion" . key "serviceInfo" . key (mkKey service) . key "defaultExec" . _Array . traverse . _String)
diff --git a/src/haskell/testdata/Arion/NixSpec/arion-compose.json b/src/haskell/testdata/Arion/NixSpec/arion-compose.json
--- a/src/haskell/testdata/Arion/NixSpec/arion-compose.json
+++ b/src/haskell/testdata/Arion/NixSpec/arion-compose.json
@@ -33,6 +33,7 @@
         }
     },
     "version": "3.4",
+    "volumes": {},
     "x-arion": {
         "images": [
             {
diff --git a/src/nix/lib.nix b/src/nix/lib.nix
--- a/src/nix/lib.nix
+++ b/src/nix/lib.nix
@@ -1,16 +1,19 @@
 { lib }:
 let
 
-  link = url: text:
-    ''link:${url}[${text}]'';
+  link = url: text: ''[${text}](${url})'';
 
-  dockerComposeRef = fragment:
-    ''See ${link "https://docs.docker.com/compose/compose-file/#${fragment}" "Docker Compose#${fragment}"}'';
+  serviceRef = fragment:
+    ''See ${link "https://docs.docker.com/compose/compose-file/05-services/#${fragment}" "Docker Compose Services #${fragment}"}'';
 
+  networkRef = fragment:
+    ''See ${link "https://docs.docker.com/compose/compose-file/06-networks/#${fragment}" "Docker Compose Network #${fragment}"}'';
+
 in
 {
   inherit
-    dockerComposeRef
     link
+    networkRef
+    serviceRef
     ;
 }
diff --git a/src/nix/modules/composition/composition.nix b/src/nix/modules/composition/composition.nix
--- a/src/nix/modules/composition/composition.nix
+++ b/src/nix/modules/composition/composition.nix
@@ -3,7 +3,7 @@
   inherit (lib) types mkOption;
 
   link = url: text:
-    ''link:${url}[${text}]'';
+    ''[${text}](${url})'';
 
 in
 {
diff --git a/src/nix/modules/composition/docker-compose.nix b/src/nix/modules/composition/docker-compose.nix
--- a/src/nix/modules/composition/docker-compose.nix
+++ b/src/nix/modules/composition/docker-compose.nix
@@ -63,6 +63,11 @@
       type = lib.types.attrsOf (lib.types.submodule service);
       description = "An attribute set of service configurations. A service specifies how to run an image as a container.";
     };
+    docker-compose.volumes = lib.mkOption {
+      type = lib.types.attrsOf lib.types.unspecified;
+      description = "A attribute set of volume configurations.";
+      default = {};
+    };
   };
   config = {
     out.dockerComposeYaml = pkgs.writeText "docker-compose.yaml" config.out.dockerComposeYamlText;
@@ -73,6 +78,7 @@
       version = "3.4";
       services = lib.mapAttrs (k: c: c.out.service) config.services;
       x-arion = config.docker-compose.extended;
+      volumes = config.docker-compose.volumes;
     };
   };
 }
diff --git a/src/nix/modules/composition/host-environment.nix b/src/nix/modules/composition/host-environment.nix
--- a/src/nix/modules/composition/host-environment.nix
+++ b/src/nix/modules/composition/host-environment.nix
@@ -23,9 +23,9 @@
         stored at an alternate location without altering the format of
         store paths.
 
-        For example: instead of mounting the host's /nix/store as the
-        container's /nix/store, this will mount /mnt/foo/nix/store
-        as the container's /nix/store.
+        For example: instead of mounting the host's `/nix/store` as the
+        container's `/nix/store`, this will mount `/mnt/foo/nix/store`
+        as the container's `/nix/store`.
       '';
     };
 
diff --git a/src/nix/modules/composition/images.nix b/src/nix/modules/composition/images.nix
--- a/src/nix/modules/composition/images.nix
+++ b/src/nix/modules/composition/images.nix
@@ -36,7 +36,7 @@
     build.imagesToLoad = lib.mkOption {
       type = listOf unspecified;
       internal = true;
-      description = "List of dockerTools image derivations.";
+      description = "List of `dockerTools` image derivations.";
     };
   };
   config = {
diff --git a/src/nix/modules/composition/networks.nix b/src/nix/modules/composition/networks.nix
--- a/src/nix/modules/composition/networks.nix
+++ b/src/nix/modules/composition/networks.nix
@@ -7,7 +7,7 @@
     types
     ;
   inherit (import ../../lib.nix { inherit lib; })
-    dockerComposeRef
+    link
     ;
 in
 {
@@ -19,7 +19,7 @@
         ];
       });
       description = ''
-        ${dockerComposeRef "networks-top-level-element"}
+        See ${link "https://docs.docker.com/compose/compose-file/06-networks/" "Docker Compose Networks"}
       '';
     };
     enableDefaultNetwork = mkOption {
diff --git a/src/nix/modules/networks/network.nix b/src/nix/modules/networks/network.nix
--- a/src/nix/modules/networks/network.nix
+++ b/src/nix/modules/networks/network.nix
@@ -7,7 +7,7 @@
     types
     ;
   inherit (import ../../lib.nix { inherit lib; })
-    dockerComposeRef
+    networkRef
     ;
 in
 {
@@ -15,21 +15,21 @@
     driver = mkOption {
       description = ''
         `"none"`, `"host"`, or a platform-specific value.
-        ${dockerComposeRef "driver"}
+        ${networkRef "driver"}
       '';
       type = types.str;
     };
 
     driver_opts = mkOption {
       description = ''
-        ${dockerComposeRef "driver_opts"}
+        ${networkRef "driver_opts"}
       '';
       type = types.lazyAttrsOf types.raw or types.unspecified;
     };
 
     attachable = mkOption {
       description = ''
-        ${dockerComposeRef "attachable"}
+        ${networkRef "attachable"}
       '';
       type = types.bool;
       example = true;
@@ -39,7 +39,7 @@
       description = ''
         Whether we've entered the 21st century yet.
         
-        ${dockerComposeRef "enable_ipv6"}
+        ${networkRef "enable_ipv6"}
       '';
       type = types.bool;
     };
@@ -49,7 +49,7 @@
       description = ''
         Manage IP addresses.
 
-        ${dockerComposeRef "ipam"}
+        ${networkRef "ipam"}
       '';
       type = types.raw or types.unspecified;
     };
@@ -58,7 +58,7 @@
       description = ''
         Achieves "external isolation".
 
-        ${dockerComposeRef "internal"}
+        ${networkRef "internal"}
       '';
       defaultText = false;
       type = types.bool;
@@ -68,7 +68,7 @@
       description = ''
         Metadata.
 
-        ${dockerComposeRef "labels"}
+        ${networkRef "labels"}
       '';
       # no list support, because less expressive wrt overriding
       type = types.attrsOf types.str;
@@ -79,7 +79,7 @@
         When `true`, don't create or destroy the network, but assume that it
         exists.
 
-        ${dockerComposeRef "external"}
+        ${networkRef "external"}
       '';
       type = types.bool;
     };
@@ -92,7 +92,7 @@
 
         Note the `default` network's default `name` is set to `project.name` by Arion.
 
-        ${dockerComposeRef "name"}
+        ${networkRef "name"}
       '';
       type = types.str;
     };
diff --git a/src/nix/modules/service/docker-compose-service.nix b/src/nix/modules/service/docker-compose-service.nix
--- a/src/nix/modules/service/docker-compose-service.nix
+++ b/src/nix/modules/service/docker-compose-service.nix
@@ -12,15 +12,9 @@
 
   inherit (import ../../lib.nix { inherit lib; })
     link
-    dockerComposeRef
+    serviceRef
     ;
 
-  dockerComposeKitchenSink = ''
-    Analogous to the `docker run` counterpart.
-
-    ${dockerComposeRef "domainname-hostname-ipc-mac_address-privileged-read_only-shm_size-stdin_open-tty-user-working_dir"}
-  '';
-
   cap_add = lib.attrNames (lib.filterAttrs (name: value: value == true) config.service.capabilities);
   cap_drop = lib.attrNames (lib.filterAttrs (name: value: value == false) config.service.capabilities);
 
@@ -56,12 +50,12 @@
     service.volumes = mkOption {
       type = listOf types.unspecified;
       default = [];
-      description = dockerComposeRef "volumes";
+      description = serviceRef "volumes";
     };
     service.tmpfs = mkOption {
       type = listOf types.str;
       default = [];
-      description = dockerComposeRef "tmpfs";
+      description = serviceRef "tmpfs";
     };
     service.build.context = mkOption {
       type = nullOr str;
@@ -69,44 +63,48 @@
       description = ''
         Locates a Dockerfile to use for creating an image to use in this service.
 
-        ${dockerComposeRef "context"}
+        https://docs.docker.com/compose/compose-file/build/#context
       '';
     };
     service.hostname = mkOption {
       type = nullOr str;
       default = null;
-      description = dockerComposeKitchenSink;
+      description = ''
+        ${serviceRef "hostname"}
+      '';
     };
     service.tty = mkOption {
       type = nullOr bool;
       default = null;
-      description = dockerComposeKitchenSink;
+      description = ''
+        ${serviceRef "tty"}
+      '';
     };
     service.environment = mkOption {
       type = attrsOf (either str int);
       default = {};
-      description = dockerComposeRef "environment";
+      description = serviceRef "environment";
     };
     service.image = mkOption {
       type = str;
-      description = dockerComposeRef "image";
+      description = serviceRef "image";
     };
     service.command = mkOption {
       type = nullOr types.unspecified;
       default = null;
-      description = dockerComposeRef "command";
+      description = serviceRef "command";
     };
     service.container_name = mkOption {
       type = nullOr types.str;
       default = null;
-      description = dockerComposeRef "container_name";
+      description = serviceRef "container_name";
     };
     service.depends_on =
       let conditionsModule = {
             options = {
               condition = mkOption {
                 type = enum ["service_started" "service_healthy" "service_completed_successfully"];
-                description = dockerComposeRef "depends_on";
+                description = serviceRef "depends_on";
                 default = "service_started";
               };
             };
@@ -114,10 +112,10 @@
        in mkOption {
          type = either (listOf str) (attrsOf (submodule conditionsModule));
          default = [];
-         description = dockerComposeRef "depends_on";
+         description = serviceRef "depends_on";
        };
     service.healthcheck = mkOption {
-      description = dockerComposeRef "healthcheck";
+      description = serviceRef "healthcheck";
       type = submodule ({ config, options, ...}: {
         options = {
           _out = mkOption {
@@ -130,30 +128,30 @@
             type = nullOr (listOf str);
             default = null;
             example = [ "CMD" "pg_isready" ];
-            description = dockerComposeRef "healthcheck";
+            description = serviceRef "healthcheck";
           };
           interval = mkOption {
             type = str;
             default = "30s";
             example = "1m";
-            description = dockerComposeRef "healthcheck";
+            description = serviceRef "healthcheck";
           };
           timeout = mkOption {
             type = str;
             default = "30s";
             example = "10s";
-            description = dockerComposeRef "healthcheck";
+            description = serviceRef "healthcheck";
           };
           start_period = mkOption {
             type = str;
             default = "0s";
             example = "30s";
-            description = dockerComposeRef "healthcheck";
+            description = serviceRef "healthcheck";
           };
           retries = mkOption {
             type = int;
             default = 3;
-            description = dockerComposeRef "healthcheck";
+            description = serviceRef "healthcheck";
           };
         };
       });
@@ -165,14 +163,14 @@
         See ${link "https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities"
         "`docker run --device` documentation"}
 
-        ${dockerComposeRef "devices"}
+        ${serviceRef "devices"}
       '';
     };
     service.dns = mkOption {
       type = listOf str;
       default = [];
       example = [ "8.8.8.8" "8.8.4.4" ];
-      description = dockerComposeRef "dns";
+      description = serviceRef "dns";
     };
     service.labels = mkOption {
       type = attrsOf str;
@@ -183,47 +181,53 @@
         "traefik.http.routers.my-service.rule" = "Host(`my-service.localhost`)";
         "traefik.http.routers.my-service.entrypoints" = "web";
       };
-      description = dockerComposeRef "labels";
+      description = serviceRef "labels";
     };
     service.links = mkOption {
       type = listOf str;
       default = [];
-      description = dockerComposeRef "links";
+      description = serviceRef "links";
     };
     service.external_links = mkOption {
       type = listOf str;
       default = [];
-      description = dockerComposeRef "external_links";
+      description = serviceRef "external_links";
     };
     service.extra_hosts = mkOption {
       type = listOf str;
       default = [];
-      description = dockerComposeRef "extra_hosts";
+      description = serviceRef "extra_hosts";
     };
     service.working_dir = mkOption {
       type = nullOr str;
       default = null;
-      description = dockerComposeKitchenSink;
+      description = ''
+        ${serviceRef "working_dir"}
+      '';
     };
     service.privileged = mkOption {
       type = nullOr bool;
       default = null;
-      description = dockerComposeKitchenSink;
+      description = ''
+        ${serviceRef "privileged"}
+      '';
     };
     service.entrypoint = mkOption {
       type = nullOr str;
       default = null;
-      description = dockerComposeRef "entrypoint";
+      description = serviceRef "entrypoint";
     };
     service.restart = mkOption {
       type = nullOr str;
       default = null;
-      description = dockerComposeRef "restart";
+      description = serviceRef "restart";
     };
     service.user = mkOption {
       type = nullOr str;
       default = null;
-      description = dockerComposeKitchenSink;
+      description = ''
+        ${serviceRef "user"}
+      '';
     };
     service.ports = mkOption {
       type = listOf types.unspecified;
@@ -231,38 +235,71 @@
       description = ''
         Expose ports on host. "host:container" or structured.
 
-        ${dockerComposeRef "ports"}
+        ${serviceRef "ports"}
       '';
     };
     service.expose = mkOption {
       type = listOf str;
       default = [];
-      description = dockerComposeRef "expose";
+      description = serviceRef "expose";
     };
     service.env_file = mkOption {
       type = listOf str;
       default = [];
-      description = dockerComposeRef "env_file";
+      description = serviceRef "env_file";
     };
     service.network_mode = mkOption {
       type = nullOr str;
       default = null;
-      description = dockerComposeRef "network_mode";
-    };
-    service.networks = mkOption {
-      type = nullOr (listOf types.str);
-      default = null;
-      description = dockerComposeRef "networks";
+      description = serviceRef "network_mode";
     };
+    service.networks =
+      let
+        networksModule = submodule ({ config, options, ...}: {
+          options = {
+            _out = mkOption {
+              internal = true;
+              readOnly = true;
+              default = lib.mapAttrs (k: opt: opt.value) (lib.filterAttrs (_: opt: opt.isDefined) { inherit (options) aliases ipv4_address ipv6_address link_local_ips priority; });
+            };
+            aliases = mkOption {
+              type = listOf str;
+              description = serviceRef "aliases";
+              default = [ ];
+            };
+            ipv4_address = mkOption {
+              type = str;
+              description = serviceRef "ipv4_address-ipv6_address";
+            };
+            ipv6_address = mkOption {
+              type = str;
+              description = serviceRef "ipv4_address-ipv6_address";
+            };
+            link_local_ips = mkOption {
+              type = listOf str;
+              description = serviceRef "link_local_ips";
+            };
+            priority = mkOption {
+              type = int;
+              description = serviceRef "priority";
+            };
+          };
+        });
+      in
+      mkOption {
+        type = either (listOf str) (attrsOf networksModule);
+        default = [];
+        description = serviceRef "networks";
+      };
     service.stop_signal = mkOption {
       type = nullOr str;
       default = null;
-      description = dockerComposeRef "stop_signal";
+      description = serviceRef "stop_signal";
     };
     service.sysctls = mkOption {
       type = attrsOf (either str int);
       default = {};
-      description = dockerComposeRef "sysctls";
+      description = serviceRef "sysctls";
     };
     service.capabilities = mkOption {
       type = attrsOf (nullOr bool);
@@ -273,13 +310,15 @@
 
         Setting a capability to `true` means that it will be
         "added". Setting it to `false` means that it will be "dropped".
-        ${dockerComposeRef "cap_add-cap_drop"}
 
         Omitted and `null` capabilities will therefore be set
         according to Docker's ${
           link "https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities"
                "default list of capabilities."
         }
+
+        ${serviceRef "cap_add"}
+        ${serviceRef "cap_drop"}
       '';
     };
   };
@@ -331,8 +370,10 @@
     inherit (config.service) privileged;
   } // lib.optionalAttrs (config.service.network_mode != null) {
     inherit (config.service) network_mode;
-  } // lib.optionalAttrs (config.service.networks != null) {
-    inherit (config.service) networks;
+  } // lib.optionalAttrs (config.service.networks != [] && config.service.networks != {}) {
+    networks =
+      if (builtins.isAttrs config.service.networks) then builtins.mapAttrs (_: v: v._out) config.service.networks
+      else config.service.networks;
   } // lib.optionalAttrs (config.service.restart != null) {
     inherit (config.service) restart;
   } // lib.optionalAttrs (config.service.stop_signal != null) {
diff --git a/src/nix/modules/service/extended-info.nix b/src/nix/modules/service/extended-info.nix
--- a/src/nix/modules/service/extended-info.nix
+++ b/src/nix/modules/service/extended-info.nix
@@ -12,7 +12,7 @@
       type = attrsOf unspecified;
       description = ''
         Information about a service to include in the Docker Compose file,
-        but that will not be used by the `docker-compose`> command
+        but that will not be used by the `docker-compose` command
         itself.
 
         It will be inserted in `x-arion.serviceInfo.<service.name>`.
diff --git a/src/nix/modules/service/host-store.nix b/src/nix/modules/service/host-store.nix
--- a/src/nix/modules/service/host-store.nix
+++ b/src/nix/modules/service/host-store.nix
@@ -20,7 +20,7 @@
     service.hostStoreAsReadOnly = mkOption {
       type = types.bool;
       default = true;
-      description = "Adds a ':ro' (read-only) access mode to the host nix store bind mount.";
+      description = "Adds a `:ro` (read-only) access mode to the host nix store bind mount.";
     };
     service.useHostNixDaemon = mkOption {
       type = types.bool;
diff --git a/src/nix/modules/service/image.nix b/src/nix/modules/service/image.nix
--- a/src/nix/modules/service/image.nix
+++ b/src/nix/modules/service/image.nix
@@ -30,6 +30,7 @@
         {
           name = null; tag = null; contents = null; config = null;
           created = null; extraCommands = null; maxLayers = null;
+          fakeRootCommands = null;
         }
         args;
       acceptedArgs = functionArgs dockerTools.streamLayeredImage;
@@ -67,6 +68,8 @@
           ln -s $i nix/var/nix/gcroots/docker/$(basename $i)
         done;
       '';
+
+    fakeRootCommands = config.image.fakeRootCommands;
   };
 
   priorityIsDefault = option: option.highestPrio >= (lib.mkDefault true).priority;
@@ -76,18 +79,18 @@
     build.image = mkOption {
       type = nullOr package;
       description = ''
-        Docker image derivation to be `docker load`ed.
+        Docker image derivation to be `docker load`-ed.
       '';
       internal = true;
     };
     build.imageName = mkOption {
       type = str;
-      description = "Derived from build.image";
+      description = "Derived from `build.image`";
       internal = true;
     };
     build.imageTag = mkOption {
       type = str;
-      description = "Derived from build.image";
+      description = "Derived from `build.image`";
       internal = true;
     };
     image.nixBuild = mkOption {
@@ -120,13 +123,22 @@
          Top level paths in the container.
       '';
     };
+    image.fakeRootCommands = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Commands that build the root of the container in the current working directory.
+
+        See [`dockerTools.buildLayeredImage`](https://nixos.org/manual/nixpkgs/stable/#ssec-pkgs-dockerTools-buildLayeredImage).
+      '';
+    };
     image.includeStorePaths = mkOption {
       type = bool;
       default = true;
       internal = true;
       description = ''
         Include all referenced store paths. You generally want this in your
-        image, unless you load store paths via some other means, like useHostStore = true;
+        image, unless you load store paths via some other means, like `useHostStore = true`;
       '';
     };
     image.rawConfig = mkOption {
@@ -140,8 +152,8 @@
         Please use the specific `image` options instead.
 
         Run-time configuration of the container. A full list of the
-        options is available in the https://github.com/moby/moby/blob/master/image/spec/v1.2.md#image-json-field-descriptions[Docker Image Specification
-        v1.2.0].
+        options is available in the [Docker Image Specification
+        v1.2.0](https://github.com/moby/moby/blob/master/image/spec/v1.2.md#image-json-field-descriptions).
       '';
     };
     image.command = mkOption {
