Mailcow: Move to Nix and Docker-Compose.
diff --git a/images.nix b/images.nix
new file mode 100644
index 0000000..186dcf1
--- /dev/null
+++ b/images.nix
@@ -0,0 +1,417 @@
+{ system ? builtins.currentSystem }:
+let
+  pkgs = import <nixpkgs> { inherit system; };
+
+in
+let
+  img = spec: {
+    streamed = pkgs.dockerTools.streamLayeredImage spec;
+    layered = pkgs.dockerTools.buildLayeredImage spec;
+    image = pkgs.dockerTools.buildImage spec;
+  };
+
+in
+{
+
+  #  ejabberd = pkgs.dockerTools.buildImage {
+  #    name = "docker.benkard.de/mulk/ejabberd";
+  #    tag = "latest";
+  #    contents = [
+  #      pkgs.ejabberd
+  #      pkgs.bash
+  #      pkgs.nano
+  #    ];
+  #    config = {
+  #      Env = [ ];
+  #      ExposedPorts = { };
+  #      WorkingDir = "/";
+  #      Volumes = {
+  #        "/data" = { };
+  #      };
+  #    };
+  #  };
+
+  prosody = img {
+    name = "docker.benkard.de/mulk/prosody";
+    #tag = "latest";
+    contents = with pkgs; [
+      prosody
+      bash
+      coreutils
+      nano
+    ];
+    config = {
+      Entrypoint = [ "/bin/bash" ];
+      Cmd = [ ];
+      Env = [ ];
+      ExposedPorts = { };
+      WorkingDir = "/";
+      Volumes = {
+        "/data" = { };
+      };
+    };
+  };
+
+  mailcow =
+    let
+      dockerComposeOverrideYaml =
+        pkgs.writeTextDir "docker-compose.override.yml" ''
+          services:
+            mysql-mailcow:
+              image: alpine/socat:1.0.3
+              command:
+                - UNIX-LISTEN:/var/run/mysqld/mysqld.sock,reuseaddr,fork,unlink-early,mode=0777
+                - TCP-CONNECT:mysql.system.svc.cluster.local.:3306
+              volumes:
+                - mysql-socket-vol-1:/var/run/mysqld/:Z
+              restart: always
+
+          volumes:
+            vmail-vol-1:                {driver: local, driver_opts: {o: bind, type: none, device: "/vol/vmail"}}
+            vmail-index-vol-1:          {driver: local, driver_opts: {o: bind, type: none, device: "/vol/vmail-index"}}
+            mysql-vol-1:                {driver: local, driver_opts: {o: bind, type: none, device: "/run/mysql"}}
+            mysql-socket-vol-1:         {driver: local, driver_opts: {o: bind, type: none, device: "/run/mysql-socket"}}
+            redis-vol-1:                {driver: local, driver_opts: {o: bind, type: none, device: "/vol/redis-data"}}
+            rspamd-vol-1:               {driver: local, driver_opts: {o: bind, type: none, device: "/vol/rspamd-data"}}
+            solr-vol-1:                 {driver: local, driver_opts: {o: bind, type: none, device: "/vol/solr-data"}}
+            postfix-vol-1:              {driver: local, driver_opts: {o: bind, type: none, device: "/vol/postfix-data"}}
+            crypt-vol-1:                {driver: local, driver_opts: {o: bind, type: none, device: "/vol/crypt-data"}}
+            sogo-web-vol-1:             {driver: local, driver_opts: {o: bind, type: none, device: "/vol/sogo-web"}}
+            sogo-userdata-backup-vol-1: {driver: local, driver_opts: {o: bind, type: none, device: "/vol/sogo-userdata-backup"}}
+        '';
+
+      init =
+        pkgs.writeShellScriptBin "init" ''
+          set -xeuo pipefail
+
+          if ! [ -e /vol/docker-data/docker.ext4 ]; then
+              ${pkgs.busybox}/bin/dd if=/dev/zero of=/vol/docker-data/docker.ext4 bs=1G count=0 seek=30
+              ${pkgs.e2fsprogs}/bin/mkfs.ext4 /vol/docker-data/docker.ext4
+          fi
+          ${pkgs.e2fsprogs}/bin/e2fsck -y /vol/docker-data/docker.ext4
+          ${pkgs.busybox}/bin/mkdir -p /var/lib/docker
+          ${pkgs.busybox}/bin/mount -o loop,rw /vol/docker-data/docker.ext4 /var/lib/docker
+
+          ${pkgs.docker}/bin/dockerd --storage-driver=overlay2 &
+          sleep 10s
+
+          ${pkgs.docker}/bin/docker kill $(${pkgs.docker}/bin/docker ps -a -q) || :
+          ${pkgs.docker}/bin/docker system prune --volumes --force || :
+
+          ${pkgs.busybox}/bin/mkdir -p /tmp /run/{mysql,mysql-socket}
+          exec ${pkgs.docker-compose}/bin/docker-compose --env-file /mailcow-dockerized/mailcow.conf -f /mailcow-dockerized/docker-compose.yml -f ${dockerComposeOverrideYaml}/docker-compose.override.yml up --remove-orphans
+        '';
+
+      src = ./mailcow/src;
+
+      extraDeps = with pkgs; [
+        # for Docker
+        cacert
+
+        # for update.sh
+        bash
+        coreutils
+        curl
+        docker
+        docker-compose
+        findutils
+        gawk
+        gitMinimal
+      ];
+
+      maintenanceDeps = with pkgs; [
+        bash
+        busybox
+        coreutils
+        findutils
+        pxattr
+        strace
+      ];
+    in
+    img {
+      name = "docker.benkard.de/mulk/mailcow";
+      tag = "latest";
+      maxLayers = 125;
+      contents = extraDeps ++ maintenanceDeps;
+      extraCommands =
+        ''
+          #!${pkgs.runtimeShell}
+
+          install -dm755 vol/{crypt-data,postfix-data,redis-data,rspamd-data,sogo-web,sogo-userdata-backup,solr-data,vmail,vmail-index,web-data}
+
+          cp -a ${src}/* .
+        '';
+      config = {
+        Entrypoint = [ "${init}/bin/init" ];
+        Cmd = [ ];
+        Workdir = "/mailcow-dockerized";
+        Volumes = {
+          "/mailcow-dockerized/data/conf" = { };
+          "/mailcow-dockerized/data/assets/ssl" = { };
+          "/vol/crypt-data" = { };
+          "/vol/docker-data" = { };
+          "/vol/postfix-data" = { };
+          "/vol/redis-data" = { };
+          "/vol/rspamd-data" = { };
+          "/vol/sogo-web" = { };
+          "/vol/sogo-userdata-backup" = { };
+          "/vol/solr-data" = { };
+          "/vol/vmail" = { };
+          "/vol/vmail-index" = { };
+          "/vol/web-data" = { };
+        };
+      };
+    };
+
+  nextcloud = img {
+    name = "docker.benkard.de/mulk/nextcloud";
+    contents =
+      let
+        baseDependencies = with pkgs; [
+          # Service dependencies.
+          apacheHttpd
+          apacheHttpdPackages.php
+
+          # Optional dependencies.
+          ffmpeg
+
+          # Maintenance and manual upgrades.
+          bash
+          coreutils
+          php
+          unzip
+        ];
+
+        phpModules = with pkgs.php74Extensions; [
+          # Required dependencies.
+          ctype
+          curl
+          dom
+          gd
+          iconv
+          json
+          mbstring
+          openssl
+          pdo_pgsql
+          posix
+          session
+          simplexml
+          xml
+          xmlreader
+          xmlwriter
+          zip
+          zlib
+
+          # Recommended dependencies.
+          bz2
+          intl
+          fileinfo
+
+          # Optional dependencies.
+          apcu
+          bcmath
+          ftp
+          gmp
+          imagick
+          memcached
+          pcntl
+          redis
+          #smbclient
+        ];
+      in
+      baseDependencies ++ phpModules;
+    config = {
+      WorkingDir = "/var/www/html";
+      Volumes = {
+        "/var/www/html" = { };
+      };
+    };
+  };
+
+  webcron = img {
+    name = "docker.benkard.de/mulk/webcron";
+    contents =
+      with pkgs; [
+        # Entry points.
+        curl
+      ];
+    config = {
+      Entrypoint = [ "curl" "-fsS" ];
+      Cmd = [ ];
+      Volumes = { };
+    };
+  };
+
+  samba =
+    let
+      runner =
+        pkgs.stdenv.mkDerivation {
+          name = "mulk-samba-runner";
+          buildInputs = with pkgs; [ bash ];
+          src = ./samba;
+          builder = builtins.toFile "builder.sh" ''
+            source $stdenv/setup
+            set -euo pipefail
+            set -x
+
+            install -Dm755 $src/init $out/init
+
+            for svc in avahi dbus nmbd smbd; do
+                install -Dm755 $src/service/$svc/run $out/service/$svc/run
+            done
+
+            set +x
+          '';
+        };
+
+    in
+    img {
+      name = "docker.benkard.de/mulk/samba";
+      contents = with pkgs; [
+        # Services.
+        avahi
+        dbus
+        #samba4Full
+        (samba.override { enableMDNS = true; enableProfiling = false; enableRegedit = false; })
+
+        # Control.
+        execline
+        gnused
+        runner
+        s6
+
+        # Maintenance.
+        busybox
+      ];
+      extraCommands =
+        let
+          dbusSystemConf =
+            builtins.toFile "dbus-1-system.conf" ''
+              <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+                                         "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+              <busconfig>
+                <type>system</type>
+                <auth>ANONYMOUS</auth>
+                <!-- <auth>EXTERNAL</auth> -->
+                <allow_anonymous/>
+                <listen>unix:path=/run/dbus/system_bus_socket</listen>
+                <standard_system_servicedirs/>
+
+                <policy context="default">
+                  <allow user="*"/>
+
+                  <deny own="*"/>
+                  <deny send_type="method_call"/>
+              
+                  <allow send_type="signal"/>
+                  <allow send_requested_reply="true" send_type="method_return"/>
+                  <allow send_requested_reply="true" send_type="error"/>
+              
+                  <allow receive_type="method_call"/>
+                  <allow receive_type="method_return"/>
+                  <allow receive_type="error"/>
+                  <allow receive_type="signal"/>
+              
+                  <allow send_destination="org.freedesktop.DBus"
+                         send_interface="org.freedesktop.DBus" />
+                  <allow send_destination="org.freedesktop.DBus"
+                         send_interface="org.freedesktop.DBus.Introspectable"/>
+                  <allow send_destination="org.freedesktop.DBus"
+                         send_interface="org.freedesktop.DBus.Properties"/>
+
+                  <deny send_destination="org.freedesktop.DBus"
+                        send_interface="org.freedesktop.DBus"
+                        send_member="UpdateActivationEnvironment"/>
+                  <deny send_destination="org.freedesktop.DBus"
+                        send_interface="org.freedesktop.DBus.Debug.Stats"/>
+                  <deny send_destination="org.freedesktop.DBus"
+                        send_interface="org.freedesktop.systemd1.Activator"/>
+                </policy>
+
+                <policy context="default">
+                  <allow own="org.freedesktop.Avahi"/>
+                </policy>
+
+                <includedir>/share/dbus-1/system.d</includedir>
+              </busconfig>
+            '';
+
+          avahiDaemonConf =
+            builtins.toFile "avahi-daemon.conf" ''
+              [server]
+              use-ipv4=yes
+              use-ipv6=yes
+              enable-dbus=yes
+              ratelimit-interval-usec=1000000
+              ratelimit-burst=1000
+              
+              [wide-area]
+              enable-wide-area=no
+              
+              [publish]
+              add-service-cookie=no
+              publish-addresses=no
+              publish-hinfo=no
+              publish-workstation=no
+              publish-domain=no
+              publish-aaaa-on-ipv4=yes
+              publish-a-on-ipv6=no
+              
+              [reflector]
+              
+              [rlimits]
+            '';
+
+          group =
+            builtins.toFile "group" ''
+              dbus::997:
+              avahi::998:
+            '';
+
+          passwd =
+            builtins.toFile "passwd" ''
+              dbus::997:997::/tmp:/nonexistent
+              avahi::998:998::/tmp:/nonexistent
+              nobody::999:999::/tmp:/nonexistent
+            '';
+        in
+        ''
+          #!${pkgs.runtimeShell}
+
+          rm -rf -- etc/avahi/services/*
+
+          install -dm755 tmp run run/dbus var/run/samba var/log/samba var/lock/samba var/locks/samba var/lib/samba/private var/cache/samba
+
+          touch var/lib/samba/registry.tdb var/lib/samba/account_policy.tdb
+
+          install -Dm644 ${dbusSystemConf} etc/dbus-1/system.conf
+          install -Dm644 ${avahiDaemonConf} etc/avahi/avahi-daemon.conf
+          install -Dm644 ${group} etc/group
+          install -Dm644 ${passwd} etc/passwd
+        '';
+      config = {
+        Entrypoint = [ "/init" ];
+        Cmd = [ ];
+        Volumes = {
+          "/vol/shares" = { };
+        };
+      };
+    };
+
+  #  nano = img {
+  #    name = "docker.benkard.de/mulk/nano";
+  #    tag = "latest";
+  #    contents = [
+  #      pkgs.nano
+  #    ];
+  #  };
+  #
+  #  vim = img {
+  #    name = "docker.benkard.de/mulk/vim";
+  #    tag = "latest";
+  #    contents = [
+  #      pkgs.vim
+  #    ];
+  #  };
+
+}