Repository: mageddo/dns-proxy-server
Pull request #390 (2023-03-21 05:44:14)

Support for Type AAAA Entries (IPv6) 
Relates to #389


Patch:
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 53fd1811..23e2d87b 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -1,3 +1,6 @@
+## 3.15
+* Support for AAAA records on docker container solving.
+
 ## 3.14.5
 * Specify minimum required docker api version `1.21` (as DPS 2)
 
diff --git a/gradle.properties b/gradle.properties
index 45612cf8..eb6d4027 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,1 +1,1 @@
-version=3.14.5
+version=3.15.0-snapshot
diff --git a/src/main/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingService.java b/src/main/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingService.java
index 5f1376a9..91d76cdb 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingService.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingService.java
@@ -1,6 +1,7 @@
 package com.mageddo.dnsproxyserver.docker;
 
 import com.github.dockerjava.api.command.InspectContainerResponse;
+import com.mageddo.dnsproxyserver.server.dns.IP;
 import com.mageddo.dnsproxyserver.server.dns.solver.HostnameQuery;
 import com.mageddo.net.Networks;
 import lombok.AllArgsConstructor;
@@ -44,7 +45,7 @@ public class ContainerSolvingService {
     final var matchedContainers = this.findMatchingContainers(host);
     final var foundIp = matchedContainers
       .stream()
-      .map(this::findBestIpMatch)
+      .map(it -> this.findBestIpMatch(it, host.getVersion()))
       .findFirst()
       .orElse(null);
     log.trace("status=findDone, host={}, found={}, time={}", host, foundIp, stopWatch.getTime());
@@ -52,12 +53,18 @@ public class ContainerSolvingService {
   }
 
   public String findBestIpMatch(InspectContainerResponse inspect) {
-    return this.findBestIpMatch(inspect, buildNetworks(inspect), this.dockerDAO::findHostMachineIpRaw);
+    return this.findBestIpMatch(inspect, IP.Version.IPV4);
   }
 
+  public String findBestIpMatch(InspectContainerResponse inspect, IP.Version version) {
+    return this.findBestIpMatch(inspect, buildNetworks(inspect), this.dockerDAO::findHostMachineIpRaw, version);
+  }
 
   public String findBestIpMatch(
-    InspectContainerResponse c, Collection<String> networksNames, Supplier<String> hostMachineSup
+    InspectContainerResponse c,
+    Collection<String> networksNames,
+    Supplier<String> hostMachineSup,
+    IP.Version version
   ) {
 
     final var networks = c
@@ -69,9 +76,12 @@ public class ContainerSolvingService {
         log.debug("status=networkNotFoundForContainer, name={}", name);
         continue;
       }
-      final var ip = networks.get(name).getIpAddress();
+      final var containerNetwork = networks.get(name);
+      final String ip = Networks.findIP(containerNetwork, version);
       log.debug("status=foundIp, network={}, container={}, ip={}", name, c.getName(), ip);
-      return ip;
+      if (StringUtils.isNotBlank(ip)) {
+        return ip;
+      }
     }
     log.debug(
       "status=predefinedNetworkNotFound, action=findSecondOption, searchedNetworks={}, container={}",
@@ -92,7 +102,7 @@ public class ContainerSolvingService {
       .min(NetworkComparator::compare)
       .map(network -> {
         final var networkName = network.getName();
-        final var ip = Networks.findIpv4Address(networks.get(networkName));
+        final var ip = Networks.findIP(networks.get(networkName), version);
         log.debug(
           "status=foundIp, networks={}, networkName={}, driver={}, foundIp={}",
           networks.keySet(), networkName, network.getDriver(), ip
@@ -102,23 +112,26 @@ public class ContainerSolvingService {
       .filter(StringUtils::isNotBlank)
       .orElseGet(() -> {
         return Optional
-          .ofNullable(buildDefaultIp(c))
+          .ofNullable(buildDefaultIp(c, version))
           .orElseGet(() -> {
             final var hostIp = hostMachineSup.get();
             log.debug("status=noNetworkAvailable, usingHostMachineIp={}", hostIp);
             return hostIp;
-          })
-          ;
+          });
       })
       ;
 
   }
 
-  static String buildDefaultIp(InspectContainerResponse c) {
-    return StringUtils.trimToNull(c
-      .getNetworkSettings()
-      .getIpAddress()
-    );
+  static String buildDefaultIp(InspectContainerResponse c, IP.Version version) {
+    final var settings = c.getNetworkSettings();
+    if (settings == null) {
+      return null;
+    }
+    if (version.isIpv6()) {
+      return StringUtils.trimToNull(settings.getGlobalIPv6Address());
+    }
+    return StringUtils.trimToNull(settings.getIpAddress());
   }
 
 
diff --git a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAODefault.java b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAODefault.java
index a6081f67..d90cd0a3 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAODefault.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerDAODefault.java
@@ -30,7 +30,7 @@ public class DockerDAODefault implements DockerDAO {
 
   @Override
   public String findHostMachineIpRaw() {
-    return DockerNetworkService.findIp(this.findBestNetwork());
+    return DockerNetworkService.findGatewayIp(this.findBestNetwork());
   }
 
   @Override
@@ -63,7 +63,7 @@ public class DockerDAODefault implements DockerDAO {
     log.debug(
       "status=bestNetwork, network={}, ip={}",
       Objects.mapOrNull(network, Network::getName),
-      DockerNetworkService.findIp(network)
+      DockerNetworkService.findGatewayIp(network)
     );
     return network;
   }
diff --git a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerNetworkService.java b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerNetworkService.java
index 83e610f0..25a9a5ae 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/docker/DockerNetworkService.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/docker/DockerNetworkService.java
@@ -2,6 +2,7 @@ package com.mageddo.dnsproxyserver.docker;
 
 import com.github.dockerjava.api.model.Container;
 import com.github.dockerjava.api.model.Network;
+import com.mageddo.dnsproxyserver.utils.Ips;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 
@@ -22,15 +23,19 @@ public class DockerNetworkService {
   private final DockerNetworkDAO networkDAO;
   private final ContainerDAO containerDAO;
 
-  public static String findIp(Network network) {
+  public static String findGatewayIp(Network network) {
     if (network == null) {
       return null;
     }
     return network
       .getIpam()
       .getConfig()
-      .get(0)
-      .getGateway();
+      .stream()
+      .map(Network.Ipam.Config::getGateway)
+      .filter(Ips::isIpv4)
+      .findFirst()
+      .orElse(null)
+      ;
   }
 
   public static Boolean isHostNetwork(Container container) {
diff --git a/src/main/java/com/mageddo/dnsproxyserver/docker/domain/Drivers.java b/src/main/java/com/mageddo/dnsproxyserver/docker/domain/Drivers.java
new file mode 100644
index 00000000..3ce8f51b
--- /dev/null
+++ b/src/main/java/com/mageddo/dnsproxyserver/docker/domain/Drivers.java
@@ -0,0 +1,6 @@
+package com.mageddo.dnsproxyserver.docker.domain;
+
+public class Drivers {
+  public static final String BRIDGE = "bridge";
+  public static final String OVERLAY = "overlay";
+}
diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/IP.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/IP.java
index df0636b7..582f439d 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/IP.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/IP.java
@@ -7,11 +7,15 @@ import org.apache.commons.lang3.Validate;
 
 public class IP {
 
-  public static final int BYTES = 4;
+  public static final int IPV4_BYTES = 4;
+  public static final int IPV6_BYTES = 16;
+
   private final String ip;
+  private final Version version;
 
   public IP(String ip) {
     this.ip = ip;
+    this.version = Version.IPV4;
   }
 
   @Override
@@ -47,9 +51,9 @@ public class IP {
       return null;
     }
     Validate.isTrue(
-      data.length == BYTES,
+      data.length == IPV4_BYTES,
       "Array of bytes is not a valid IP representation, size must be %d",
-      BYTES
+      IPV4_BYTES
     );
     return of(String.format(
       "%d.%d.%d.%d",
@@ -68,4 +72,14 @@ public class IP {
   public boolean isLoopback() {
     return this.ip.startsWith("127.");
   }
+
+  public enum Version {
+    IPV4,
+    IPV6,
+    ;
+
+    public boolean isIpv6() {
+      return this == IPV6;
+    }
+  }
 }
diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/Messages.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/Messages.java
index b6594b2f..8de8f951 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/Messages.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/Messages.java
@@ -4,6 +4,7 @@ import com.mageddo.dnsproxyserver.config.Config;
 import com.mageddo.dnsproxyserver.server.dns.solver.Response;
 import com.mageddo.dnsproxyserver.utils.Ips;
 import lombok.SneakyThrows;
+import org.xbill.DNS.AAAARecord;
 import org.xbill.DNS.ARecord;
 import org.xbill.DNS.CNAMERecord;
 import org.xbill.DNS.DClass;
@@ -104,9 +105,12 @@ public class Messages {
     return section.get(0);
   }
 
-  public static Message nxDomain(Message msg) {
-    msg.getHeader().setRcode(Rcode.NXDOMAIN);
-    return msg;
+  public static Message nxDomain(Message query) {
+    final var res = query.clone();
+    final var header = res.getHeader();
+    header.setRcode(Rcode.NXDOMAIN);
+    header.setFlag(Flags.QR);
+    return res;
   }
 
   @SneakyThrows
@@ -148,6 +152,7 @@ public class Messages {
 
   /**
    * Add records from source to target for all sections
+   *
    * @return a clone with the combination.
    */
   public static Message combine(Message source, Message target) {
@@ -174,8 +179,7 @@ public class Messages {
   public static Duration findTTL(Message m) {
     final var answer = Optional
       .ofNullable(Messages.findFirstAnswerRecord(m))
-      .orElseGet(() -> Messages.findFirstAuthorityRecord(m))
-      ;
+      .orElseGet(() -> Messages.findFirstAuthorityRecord(m));
     if (answer == null) {
       return Duration.ZERO;
     }
@@ -205,4 +209,28 @@ public class Messages {
     return res;
   }
 
+  public static Message quadAnswer(Message query, String ip) {
+    return quadAnswer(query, ip, DEFAULT_TTL);
+  }
+
+  public static Message quadAnswer(Message query, String ip, final long ttl) {
+    final var res = withNoErrorResponse(query.clone());
+    final var answer = new AAAARecord(res.getQuestion().getName(), DClass.IN, ttl, Ips.toAddress(ip));
+    res.addRecord(answer, Section.ANSWER);
+    return res;
+  }
+
+  public static Message answer(Message query, String ip) {
+    if (Ips.isIpv6(ip)) {
+      return Messages.quadAnswer(query, ip);
+    }
+    return Messages.aAnswer(query, ip);
+  }
+
+  public static Message answer(Message query, String ip, IP.Version version) {
+    if (version.isIpv6()) {
+      return Messages.quadAnswer(query, ip);
+    }
+    return Messages.aAnswer(query, ip);
+  }
 }
diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/HostnameMatcher.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/HostnameMatcher.java
index 4d32933f..9ab26b9f 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/HostnameMatcher.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/HostnameMatcher.java
@@ -1,16 +1,21 @@
 package com.mageddo.dnsproxyserver.server.dns.solver;
 
 import com.mageddo.dnsproxyserver.server.dns.Hostname;
+import com.mageddo.dnsproxyserver.server.dns.IP;
 
 import java.util.List;
 import java.util.function.Function;
 
 public class HostnameMatcher {
 
-  public static <T> T match(Hostname hostname, Function<HostnameQuery, T> hostnameProviderFn) {
+  public static <T> T match(
+    Hostname hostname,
+    IP.Version version,
+    Function<HostnameQuery, T> hostnameProviderFn
+  ) {
 
-    final var wildcardHostname = HostnameQuery.ofWildcard(hostname);
-    final var regexHostname = HostnameQuery.ofRegex(hostname);
+    final var wildcardHostname = HostnameQuery.ofWildcard(hostname, version);
+    final var regexHostname = HostnameQuery.ofRegex(hostname, version);
     final var queries = List.of(wildcardHostname, regexHostname);
 
     for (final var host : queries) {
diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/HostnameQuery.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/HostnameQuery.java
index b6cce54b..f058ffe8 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/HostnameQuery.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/HostnameQuery.java
@@ -1,6 +1,7 @@
 package com.mageddo.dnsproxyserver.server.dns.solver;
 
 import com.mageddo.dnsproxyserver.server.dns.Hostname;
+import com.mageddo.dnsproxyserver.server.dns.IP;
 import com.mageddo.dnsproxyserver.server.dns.Wildcards;
 import lombok.Builder;
 import lombok.EqualsAndHashCode;
@@ -18,6 +19,9 @@ public class HostnameQuery {
   @NonNull
   private final Hostname hostname;
 
+  @NonNull
+  private final IP.Version version;
+
   private final boolean useWildcards;
 
   private final boolean useRegex;
@@ -27,19 +31,45 @@ public class HostnameQuery {
   }
 
   public static HostnameQuery ofWildcard(String hostname) {
-    return ofWildcard(Hostname.of(hostname));
+    return ofWildcard(hostname, IP.Version.IPV4);
+  }
+
+  public static HostnameQuery ofWildcard(String hostname, IP.Version version) {
+    return ofWildcard(Hostname.of(hostname), version);
   }
 
   public static HostnameQuery ofWildcard(Hostname hostname) {
-    return of(hostname, true, false);
+    return ofWildcard(hostname, IP.Version.IPV4);
+  }
+
+  public static HostnameQuery ofWildcard(Hostname hostname, IP.Version version) {
+    return builder()
+      .hostname(hostname)
+      .version(version)
+      .useWildcards(true)
+      .useRegex(false)
+      .build();
   }
 
   public static HostnameQuery ofRegex(String hostname) {
-    return ofRegex(Hostname.of(hostname));
+    return ofRegex(hostname, IP.Version.IPV4);
+  }
+
+  public static HostnameQuery ofRegex(String hostname, IP.Version version) {
+    return ofRegex(Hostname.of(hostname), version);
   }
 
   public static HostnameQuery ofRegex(Hostname hostname) {
-    return of(hostname, false, true);
+    return ofRegex(hostname, IP.Version.IPV4);
+  }
+
+  public static HostnameQuery ofRegex(Hostname hostname, IP.Version version) {
+    return builder()
+      .hostname(hostname)
+      .version(version)
+      .useWildcards(false)
+      .useRegex(true)
+      .build();
   }
 
   public static HostnameQuery of(Hostname hostname, boolean wildcards, boolean regex) {
@@ -48,6 +78,7 @@ public class HostnameQuery {
       .hostname(hostname)
       .useWildcards(wildcards)
       .useRegex(regex)
+      .version(IP.Version.IPV4)
       .build();
   }
 
diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverDocker.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverDocker.java
index ffdc25b9..d8efbe53 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverDocker.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverDocker.java
@@ -1,8 +1,10 @@
 package com.mageddo.dnsproxyserver.server.dns.solver;
 
 import com.mageddo.commons.lang.Objects;
+import com.mageddo.dnsproxyserver.config.Config.Entry.Type;
 import com.mageddo.dnsproxyserver.docker.ContainerSolvingService;
 import com.mageddo.dnsproxyserver.docker.DockerDAO;
+import com.mageddo.dnsproxyserver.server.dns.IP;
 import com.mageddo.dnsproxyserver.server.dns.Messages;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -27,12 +29,26 @@ public class SolverDocker implements Solver {
       return null;
     }
 
+    final var type = Messages.findQuestionTypeCode(query);
+    if (Type.isNot(type, Type.AAAA, Type.A)) {
+      log.trace("status=unsupportedType, type={}", type);
+      return null;
+    }
+
     final var askedHost = Messages.findQuestionHostname(query);
-    return HostnameMatcher.match(askedHost, hostname -> {
+    final var version = toVersion(Type.of(type));
+    return HostnameMatcher.match(askedHost, version, hostname -> {
       final var ip = this.containerSolvingService.findBestHostIP(hostname);
-      return Objects.mapOrNull(ip, (it) -> Response.of(Messages.aAnswer(query, ip)));
+      return Objects.mapOrNull(ip, (it) -> Response.of(Messages.answer(query, ip, hostname.getVersion())));
     });
 
   }
 
+  IP.Version toVersion(Type type) {
+    return switch (type) {
+      case A -> IP.Version.IPV4;
+      case AAAA -> IP.Version.IPV6;
+      default -> throw new IllegalStateException("Unexpected value: " + type);
+    };
+  }
 }
diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverLocalDB.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverLocalDB.java
index ccc2994f..8248a69c 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverLocalDB.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverLocalDB.java
@@ -3,6 +3,7 @@ package com.mageddo.dnsproxyserver.server.dns.solver;
 import com.mageddo.dnsproxyserver.config.Config;
 import com.mageddo.dnsproxyserver.config.Config.Entry.Type;
 import com.mageddo.dnsproxyserver.config.ConfigDAO;
+import com.mageddo.dnsproxyserver.server.dns.IP;
 import com.mageddo.dnsproxyserver.server.dns.Messages;
 import dagger.Lazy;
 import lombok.AllArgsConstructor;
@@ -34,7 +35,7 @@ public class SolverLocalDB implements Solver {
     }
 
     final var askedHost = Messages.findQuestionHostname(query);
-    final var res = HostnameMatcher.match(askedHost, hostname -> {
+    final var res = HostnameMatcher.match(askedHost, IP.Version.IPV4, hostname -> {
       stopWatch.split();
       final var entry = this.findEntryTo(hostname);
       if (entry == null) {
diff --git a/src/main/java/com/mageddo/dnsproxyserver/utils/Ips.java b/src/main/java/com/mageddo/dnsproxyserver/utils/Ips.java
index 342fe5c6..f4b45da7 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/utils/Ips.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/utils/Ips.java
@@ -11,6 +11,10 @@ import java.net.SocketAddress;
 import java.net.UnknownHostException;
 
 public class Ips {
+
+  private Ips() {
+  }
+
   public static byte[] toBytes(String ip) {
     if (StringUtils.isBlank(ip)) {
       return null;
@@ -25,7 +29,11 @@ public class Ips {
   }
 
   public static InetAddress toAddress(String ip) {
-    return toAddress(toBytes(ip));
+    try {
+      return InetAddress.getByName(ip);
+    } catch (UnknownHostException e) {
+      throw new RuntimeException(e);
+    }
   }
 
   public static InetAddress toAddress(byte[] ip) {
@@ -55,4 +63,12 @@ public class Ips {
   public static InetSocketAddress getAnyLocalAddress(int port) {
     return new InetSocketAddress(getAnyLocalAddress(), port);
   }
+
+  public static boolean isIpv6(String v) {
+    return StringUtils.trimToEmpty(v).contains(":");
+  }
+
+  public static boolean isIpv4(String v) {
+    return !isIpv6(v);
+  }
 }
diff --git a/src/main/java/com/mageddo/net/Networks.java b/src/main/java/com/mageddo/net/Networks.java
index 3e8568ca..fe5b033e 100644
--- a/src/main/java/com/mageddo/net/Networks.java
+++ b/src/main/java/com/mageddo/net/Networks.java
@@ -4,6 +4,7 @@ import com.github.dockerjava.api.model.Container;
 import com.github.dockerjava.api.model.ContainerNetwork;
 import com.mageddo.dnsproxyserver.server.dns.IP;
 import lombok.SneakyThrows;
+import org.apache.commons.lang3.StringUtils;
 
 import java.net.NetworkInterface;
 import java.net.SocketException;
@@ -24,8 +25,8 @@ public class Networks {
   }
 
   /**
-   *  The "relevance" is understood as the IP which have most chances of represent the real hardware network interface,
-   *  we say "most chances" beucase java api haven't deterministic information on that.
+   * The "relevance" is understood as the IP which have most chances of represent the real hardware network interface,
+   * we say "most chances" beucase java api haven't deterministic information on that.
    *
    * @return Machine ips ordered by relevance.
    */
@@ -34,7 +35,7 @@ public class Networks {
       .stream()
       .sorted(Comparator.comparingInt(NetworkInterface::getIndex))
       .flatMap(NetworkInterface::inetAddresses)
-      .filter(it -> it.getAddress().length == IP.BYTES) // todo needs a filter to exclude virtual network cards
+      .filter(it -> it.getAddress().length == IP.IPV4_BYTES) // todo needs a filter to exclude virtual network cards
       .map(it -> IP.of(it.getHostAddress()))
       .sorted(Comparator.comparing(it -> {
         return it.raw().startsWith("127") ? Integer.MAX_VALUE : 0;
@@ -55,6 +56,7 @@ public class Networks {
       .toList();
   }
 
+  // todo methods below are docker related methods so supposed to be on com.mageddo.dnsproxyserver.docker.net package
   public static String findIpv4Address(String networkName, Container container) {
     final var containerNetwork = findContainerNetwork(networkName, container);
     if (containerNetwork == null) {
@@ -77,7 +79,22 @@ public class Networks {
     return Optional
       .ofNullable(containerNetwork)
       .map(ContainerNetwork::getIpAddress)
+      .map(StringUtils::trimToNull)
       .orElse(null);
   }
 
+  public static String findIpv6Address(ContainerNetwork containerNetwork) {
+    return Optional
+      .ofNullable(containerNetwork)
+      .map(ContainerNetwork::getGlobalIPv6Address)
+      .map(StringUtils::trimToNull)
+      .orElse(null);
+  }
+
+  public static String findIP(ContainerNetwork network, IP.Version version) {
+    return switch (version) {
+      case IPV4 -> findIpv4Address(network);
+      case IPV6 -> findIpv6Address(network);
+    };
+  }
 }


Test patch:
diff --git a/src/test/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingServiceCompTest.java b/src/test/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingServiceCompTest.java
index 2625cef1..a4d70b9c 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingServiceCompTest.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/docker/ContainerSolvingServiceCompTest.java
@@ -1,18 +1,25 @@
 package com.mageddo.dnsproxyserver.docker;
 
 import com.mageddo.dnsproxyserver.di.Context;
+import com.mageddo.dnsproxyserver.server.dns.IP;
+import com.mageddo.dnsproxyserver.templates.IpTemplates;
 import com.mageddo.dnsproxyserver.templates.docker.InspectContainerResponseTemplates;
 import com.mageddo.dnsproxyserver.templates.docker.NetworkTemplates;
 import dagger.sheath.InjectMock;
 import dagger.sheath.junit.DaggerTest;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
 import javax.inject.Inject;
 
+import static com.mageddo.dnsproxyserver.templates.docker.InspectContainerResponseTemplates.ngixWithDefaultBridgeNetworkOnly;
+import static com.mageddo.dnsproxyserver.templates.docker.InspectContainerResponseTemplates.ngixWithIpv4DefaultBridgeAndIpv6CustomBridgeNetwork;
+import static com.mageddo.dnsproxyserver.templates.docker.InspectContainerResponseTemplates.ngixWithIpv6CustomBridgeNetwork;
+import static com.mageddo.dnsproxyserver.templates.docker.InspectContainerResponseTemplates.ngixWithIpv6DefaultBridgeNetworkOnly;
+import static com.mageddo.dnsproxyserver.templates.docker.InspectContainerResponseTemplates.ngixWithIpv6DefaultIp;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
@@ -31,14 +38,6 @@ class ContainerSolvingServiceCompTest {
   @Inject
   ContainerSolvingService containerSolvingService;
 
-  @BeforeEach
-  void beforeEach() {
-    doReturn("192.168.15.1")
-      .when(this.dockerDAO)
-      .findHostMachineIpRaw()
-    ;
-  }
-
   @Test
   void mustSolveSpecifiedNetworkFirst() {
     // arrange
@@ -59,7 +58,7 @@ class ContainerSolvingServiceCompTest {
     the bridge network.
     """)
   @Test
-  void mustPreferBridgeNetworkOverOtherNetworksWhenThereIsNotABetterMatch() {
+  void mustPreferCustomBridgeNetworkOverOtherNetworksWhenThereIsNotABetterMatch() {
     // arrange
 
     final var bridgeNetwork = "custom-bridge";
@@ -84,4 +83,104 @@ class ContainerSolvingServiceCompTest {
     verify(this.dockerNetworkDAO, never()).findById(anyString());
 
   }
+
+  @Test
+  void mustSolveFromDefaultBridgeNetwork() {
+    // arrange
+    final var inspect = ngixWithDefaultBridgeNetworkOnly();
+
+    // act
+    final var ip = this.containerSolvingService.findBestIpMatch(inspect);
+
+    // assert
+    assertNotNull(ip);
+    assertEquals("172.17.0.4", ip);
+
+  }
+
+  @Test
+  void mustSolveEmptyIpv6FromDefaultBridgeNetwork() {
+    // arrange
+    final var inspect = ngixWithDefaultBridgeNetworkOnly();
+    final var version = IP.Version.IPV6;
+
+    // act
+    final var ip = this.containerSolvingService.findBestIpMatch(inspect, version);
+
+    // assert
+    assertNull(ip);
+
+  }
+
+  @Test
+  void mustSolveIpv6FromDefaultBridgeNetwork() {
+    // arrange
+    final var inspect = ngixWithIpv6DefaultBridgeNetworkOnly();
+    final var version = IP.Version.IPV6;
+
+    // act
+    final var ip = this.containerSolvingService.findBestIpMatch(inspect, version);
+
+    // assert
+    assertNotNull(ip);
+    assertEquals("2001:db8:abc1::242:ac11:4", ip);
+
+  }
+
+  @Test
+  void mustSolveIpv6FromAnyOtherNetwork() {
+    // arrange
+    final var inspect = ngixWithIpv6CustomBridgeNetwork();
+    final var version = IP.Version.IPV6;
+
+    doReturn(NetworkTemplates.withBridgeDriver("my-net1"))
+      .when(this.dockerNetworkDAO)
+      .findByName(anyString())
+    ;
+
+    // act
+    final var ip = this.containerSolvingService.findBestIpMatch(inspect, version);
+
+    // assert
+    assertNotNull(ip);
+    assertEquals(IpTemplates.LOCAL_IPV6, ip);
+
+  }
+
+  @Test
+  void mustSolveIpv6FromDefaultIPNetwork() {
+    // arrange
+    final var inspect = ngixWithIpv6DefaultIp();
+    final var version = IP.Version.IPV6;
+
+    // act
+    final var ip = this.containerSolvingService.findBestIpMatch(inspect, version);
+
+    // assert
+    assertNotNull(ip);
+    assertEquals("2001:db7:1::2", ip);
+    verify(this.dockerDAO, never()).findHostMachineIpRaw();
+  }
+
+  @Test
+  void mustNotUseAnEmptyIpSpecifiedOnPreferredNetworks() {
+    // arrange
+    final var inspect = ngixWithIpv4DefaultBridgeAndIpv6CustomBridgeNetwork();
+    final var version = IP.Version.IPV6;
+
+    doReturn(NetworkTemplates.withBridgeDriver("my-net1"))
+      .when(this.dockerNetworkDAO)
+      .findByName(anyString())
+    ;
+
+    // act
+    final var ip = this.containerSolvingService.findBestIpMatch(inspect, version);
+
+    // assert
+    assertNotNull(ip);
+    assertEquals(IpTemplates.LOCAL_IPV6, ip);
+
+  }
+
+
 }
diff --git a/src/test/java/com/mageddo/dnsproxyserver/docker/DockerNetworkServiceTest.java b/src/test/java/com/mageddo/dnsproxyserver/docker/DockerNetworkServiceTest.java
new file mode 100644
index 00000000..418eb49d
--- /dev/null
+++ b/src/test/java/com/mageddo/dnsproxyserver/docker/DockerNetworkServiceTest.java
@@ -0,0 +1,22 @@
+package com.mageddo.dnsproxyserver.docker;
+
+import org.junit.jupiter.api.Test;
+
+import static com.mageddo.dnsproxyserver.templates.docker.NetworkTemplates.buildBridgeIpv4AndIpv6Network;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class DockerNetworkServiceTest {
+
+  @Test
+  void mustSolveIpv4AddressEvenWhenIpv6IsAvailable(){
+
+    // arrange
+    final var network = buildBridgeIpv4AndIpv6Network();
+
+    // act
+    final var ip = DockerNetworkService.findGatewayIp(network);
+
+    // assert
+    assertEquals("172.21.0.1", ip);
+  }
+}
diff --git a/src/test/java/com/mageddo/dnsproxyserver/server/dns/MessagesTest.java b/src/test/java/com/mageddo/dnsproxyserver/server/dns/MessagesTest.java
new file mode 100644
index 00000000..e0447a84
--- /dev/null
+++ b/src/test/java/com/mageddo/dnsproxyserver/server/dns/MessagesTest.java
@@ -0,0 +1,30 @@
+package com.mageddo.dnsproxyserver.server.dns;
+
+import com.mageddo.dnsproxyserver.templates.MessageTemplates;
+import org.junit.jupiter.api.Test;
+import org.xbill.DNS.Flags;
+import org.xbill.DNS.Rcode;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class MessagesTest {
+
+  @Test
+  void mustBuildCompliantNxResponse(){
+
+    // arrange
+    final var query = MessageTemplates.acmeAQuery();
+
+    // act
+    final var res = Messages.nxDomain(query);
+
+    // assert
+    assertFalse(query.getHeader().getFlag(Flags.QR));
+    assertTrue(res.getHeader().getFlag(Flags.QR));
+    assertEquals(Rcode.NXDOMAIN, res.getHeader().getRcode());
+
+  }
+
+}
diff --git a/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverDockerTest.java b/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverDockerTest.java
index 4b28b63c..53e4c8d5 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverDockerTest.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverDockerTest.java
@@ -2,16 +2,22 @@ package com.mageddo.dnsproxyserver.server.dns.solver;
 
 import com.mageddo.dnsproxyserver.docker.ContainerSolvingService;
 import com.mageddo.dnsproxyserver.docker.DockerDAO;
+import com.mageddo.dnsproxyserver.server.dns.IP;
 import com.mageddo.dnsproxyserver.templates.HostnameTemplates;
+import com.mageddo.dnsproxyserver.templates.IpTemplates;
 import com.mageddo.dnsproxyserver.templates.MessageTemplates;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
@@ -25,6 +31,9 @@ class SolverDockerTest {
   @Mock
   DockerDAO dockerDAO;
 
+  @Captor
+  ArgumentCaptor<HostnameQuery> hostnameQueryCaptor;
+
   SolverDocker solver;
 
   @BeforeEach
@@ -58,4 +67,34 @@ class SolverDockerTest {
     verify(this.containerSolvingService).findBestHostIP(hostname);
   }
 
+
+  @Test
+  void mustSolveQuadARecordQuery() {
+    // arrange
+    final var query = MessageTemplates.acmeQuadAQuery();
+    final var ip = IpTemplates.LOCAL_EXTENDED_IPV6;
+
+    doReturn(true)
+      .when(this.dockerDAO)
+      .isConnected()
+    ;
+
+    doReturn(ip)
+      .when(this.containerSolvingService)
+      .findBestHostIP(any());
+
+    // act
+    final var res = this.solver.handle(query);
+
+    // assert
+    assertNotNull(res);
+
+    final var resText = res.toString();
+    assertTrue(resText.contains(ip), resText);
+    verify(this.containerSolvingService).findBestHostIP(this.hostnameQueryCaptor.capture());
+
+    final var v = this.hostnameQueryCaptor.getValue();
+    assertEquals(IP.Version.IPV6, v.getVersion());
+  }
+
 }
diff --git a/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystemCompTest.java b/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystemCompTest.java
index 6162c24b..aaf88367 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystemCompTest.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverSystemCompTest.java
@@ -2,7 +2,6 @@ package com.mageddo.dnsproxyserver.server.dns.solver;
 
 import com.mageddo.dnsproxyserver.server.dns.IP;
 import com.mageddo.dnsproxyserver.server.dns.Messages;
-import com.mageddo.dnsproxyserver.templates.MessageTemplates;
 import com.mageddo.dnsproxyserver.usecase.HostMachineService;
 import dagger.sheath.InjectMock;
 import dagger.sheath.junit.DaggerTest;
@@ -31,7 +30,7 @@ class SolverSystemCompTest {
   void mustSolverHostMachineIp(){
     // arrange
     final var hostname = "host.docker.";
-    final var query = MessageTemplates.buildAQuestionFor(hostname);
+    final var query = Messages.aQuestion(hostname);
 
     doReturn(IP.of("192.168.0.1"))
       .when(this.machineService)
diff --git a/src/test/java/com/mageddo/dnsproxyserver/templates/IpTemplates.java b/src/test/java/com/mageddo/dnsproxyserver/templates/IpTemplates.java
index 5b0cf055..013ee185 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/templates/IpTemplates.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/templates/IpTemplates.java
@@ -5,6 +5,8 @@ import com.mageddo.dnsproxyserver.server.dns.IP;
 public class IpTemplates {
 
   public static final String LOCAL = "10.10.0.1";
+  public static final String LOCAL_IPV6 = "2001:db8:1::2";
+  public static final String LOCAL_EXTENDED_IPV6 = "2001:db8:1:0:0:0:0:2";
 
   public static IP local(){
     return IP.of(LOCAL);
diff --git a/src/test/java/com/mageddo/dnsproxyserver/templates/MessageTemplates.java b/src/test/java/com/mageddo/dnsproxyserver/templates/MessageTemplates.java
index 1b6341cf..4b785f06 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/templates/MessageTemplates.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/templates/MessageTemplates.java
@@ -1,25 +1,18 @@
 package com.mageddo.dnsproxyserver.templates;
 
 import com.mageddo.dnsproxyserver.server.dns.Messages;
-import lombok.SneakyThrows;
-import org.xbill.DNS.DClass;
 import org.xbill.DNS.Message;
-import org.xbill.DNS.Name;
-import org.xbill.DNS.Record;
-import org.xbill.DNS.Type;
 
 import static com.mageddo.dnsproxyserver.templates.HostnameTemplates.ACME_HOSTNAME;
 
 public class MessageTemplates {
 
-  @SneakyThrows
-  public static Message buildAQuestionFor(String hostname) {
-    final var r = Record.newRecord(Name.fromString(hostname), Type.A, DClass.IN, 0);
-    return Message.newQuery(r);
+  public static Message acmeAQuery() {
+    return Messages.aQuestion(ACME_HOSTNAME + ".");
   }
 
-  public static Message acmeAQuery() {
-    return buildAQuestionFor(ACME_HOSTNAME + ".");
+  public static Message acmeQuadAQuery() {
+    return Messages.quadAQuestion(ACME_HOSTNAME + ".");
   }
 
   public static Message buildAAnswer(Message query) {
diff --git a/src/test/java/com/mageddo/dnsproxyserver/templates/docker/InspectContainerResponseTemplates.java b/src/test/java/com/mageddo/dnsproxyserver/templates/docker/InspectContainerResponseTemplates.java
index 5e2b252f..a3d4946c 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/templates/docker/InspectContainerResponseTemplates.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/templates/docker/InspectContainerResponseTemplates.java
@@ -8,8 +8,37 @@ import lombok.SneakyThrows;
 
 public class InspectContainerResponseTemplates {
 
+  /**
+   * Nginx container with default bridge, dps and specific network.
+   */
   public static final String NGINX = "/templates/nginx.json";
 
+  /**
+   * {@link #NGINX} container with default bridge only
+   */
+  public static final String NGINX_002 = "/templates/nginx-002.json";
+
+  /**
+   * Nginx container with default bridge only with ipv6 support.
+   */
+  public static final String NGINX_003 = "/templates/nginx-003.json";
+
+  /**
+   * Nginx container with no default bridge but a custom network with ipv6.
+   */
+  public static final String NGINX_004 = "/templates/nginx-004.json";
+
+  /**
+   * Nginx container with only the default ip.
+   */
+  public static final String NGINX_005 = "/templates/nginx-005.json";
+
+  /**
+   * Nginx container with default bridge with ipv4 address only and a custom network with ipv6.
+   */
+  public static final String NGINX_006 = "/templates/nginx-006.json";
+
+
   public static InspectContainerResponse buildWithHostnameAndWithoutDomain() {
     return build();
   }
@@ -41,6 +70,26 @@ public class InspectContainerResponseTemplates {
     return parse();
   }
 
+  public static InspectContainerResponse ngixWithDefaultBridgeNetworkOnly() {
+    return parse(buildTree(NGINX_002));
+  }
+
+  public static InspectContainerResponse ngixWithIpv6DefaultBridgeNetworkOnly() {
+    return parse(buildTree(NGINX_003));
+  }
+
+  public static InspectContainerResponse ngixWithIpv6CustomBridgeNetwork() {
+    return parse(buildTree(NGINX_004));
+  }
+
+  public static InspectContainerResponse ngixWithIpv6DefaultIp() {
+    return parse(buildTree(NGINX_005));
+  }
+
+  public static InspectContainerResponse ngixWithIpv4DefaultBridgeAndIpv6CustomBridgeNetwork() {
+    return parse(buildTree(NGINX_006));
+  }
+
   private static InspectContainerResponse parse() {
     return parse(buildTree(NGINX));
   }
diff --git a/src/test/java/com/mageddo/dnsproxyserver/templates/docker/NetworkTemplates.java b/src/test/java/com/mageddo/dnsproxyserver/templates/docker/NetworkTemplates.java
index daaff25a..ab08eadb 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/templates/docker/NetworkTemplates.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/templates/docker/NetworkTemplates.java
@@ -2,16 +2,19 @@ package com.mageddo.dnsproxyserver.templates.docker;
 
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 import com.github.dockerjava.api.model.Network;
+import com.mageddo.dnsproxyserver.docker.domain.Drivers;
 import com.mageddo.json.JsonUtils;
 import lombok.SneakyThrows;
 
+import static com.mageddo.utils.TestUtils.readString;
+
 public class NetworkTemplates {
 
   @SneakyThrows
   public static Network withBridgeDriver(String name) {
     final var node = JsonNodeFactory.instance.objectNode()
       .put("Name", name)
-      .put("Driver", "bridge");
+      .put("Driver", Drivers.BRIDGE);
     return JsonUtils
       .instance()
       .treeToValue(node, Network.class)
@@ -19,13 +22,18 @@ public class NetworkTemplates {
   }
 
   @SneakyThrows
-  public static Object withOverlayDriver(String name) {
+  public static Network withOverlayDriver(String name) {
     final var node = JsonNodeFactory.instance.objectNode()
       .put("Name", name)
-      .put("Driver", "overlay");
+      .put("Driver", Drivers.OVERLAY);
     return JsonUtils
       .instance()
       .treeToValue(node, Network.class)
       ;
   }
+
+  public static Network buildBridgeIpv4AndIpv6Network() {
+    return JsonUtils.readValue(readString("/templates/docker/network/001.json"), Network.class);
+  }
+
 }
diff --git a/src/test/resources/templates/docker/network/001.json b/src/test/resources/templates/docker/network/001.json
new file mode 100644
index 00000000..c7b9431b
--- /dev/null
+++ b/src/test/resources/templates/docker/network/001.json
@@ -0,0 +1,40 @@
+{
+  "Name": "my-net1",
+  "Id": "0ec5522fa0b270d6d4727065a65668661b5c116d77542335e2a4497994935e05",
+  "Created": "2023-03-21T01:46:45.398806871-03:00",
+  "Scope": "local",
+  "Driver": "bridge",
+  "EnableIPv6": true,
+  "IPAM": {
+    "Driver": "default",
+    "Options": {},
+    "Config": [
+      {
+        "Subnet": "2001:db8:1::/64",
+        "Gateway": "2001:db8:1::1"
+      },
+      {
+        "Subnet": "172.21.0.0/16",
+        "Gateway": "172.21.0.1"
+      }
+    ]
+  },
+  "Internal": false,
+  "Attachable": false,
+  "Ingress": false,
+  "ConfigFrom": {
+    "Network": ""
+  },
+  "ConfigOnly": false,
+  "Containers": {
+    "68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a": {
+      "Name": "nginx1",
+      "EndpointID": "4d398f450b14c320c25f457f32c140131f5ec5bb55b6300bd261909d2f8e6aba",
+      "MacAddress": "02:42:ac:15:00:02",
+      "IPv4Address": "172.21.0.2/16",
+      "IPv6Address": "2001:db8:1::2/64"
+    }
+  },
+  "Options": {},
+  "Labels": {}
+}
diff --git a/src/test/resources/templates/nginx-002.json b/src/test/resources/templates/nginx-002.json
new file mode 100644
index 00000000..6ab2bd71
--- /dev/null
+++ b/src/test/resources/templates/nginx-002.json
@@ -0,0 +1,207 @@
+{
+  "Id": "ccb1becce0235218556b8de161d54383782f0ac6de5f83eff88d4c360068c536",
+  "Created": "2023-02-07T04:16:51.617271116Z",
+  "Path": "bash",
+  "Args": [],
+  "State": {
+    "Status": "running",
+    "Running": true,
+    "Paused": false,
+    "Restarting": false,
+    "OOMKilled": false,
+    "Dead": false,
+    "Pid": 154654,
+    "ExitCode": 0,
+    "Error": "",
+    "StartedAt": "2023-02-07T04:16:52.011328278Z",
+    "FinishedAt": "0001-01-01T00:00:00Z"
+  },
+  "Image": "sha256:53f3fd8007f76bd23bf663ad5f5009c8941f63828ae458cef584b5f85dc0a7bf",
+  "ResolvConfPath": "/var/lib/docker/containers/ccb1becce0235218556b8de161d54383782f0ac6de5f83eff88d4c360068c536/resolv.conf",
+  "HostnamePath": "/var/lib/docker/containers/ccb1becce0235218556b8de161d54383782f0ac6de5f83eff88d4c360068c536/hostname",
+  "HostsPath": "/var/lib/docker/containers/ccb1becce0235218556b8de161d54383782f0ac6de5f83eff88d4c360068c536/hosts",
+  "LogPath": "/var/lib/docker/containers/ccb1becce0235218556b8de161d54383782f0ac6de5f83eff88d4c360068c536/ccb1becce0235218556b8de161d54383782f0ac6de5f83eff88d4c360068c536-json.log",
+  "Name": "/laughing_swanson",
+  "RestartCount": 0,
+  "Driver": "overlay2",
+  "Platform": "linux",
+  "MountLabel": "",
+  "ProcessLabel": "",
+  "AppArmorProfile": "docker-default",
+  "ExecIDs": null,
+  "HostConfig": {
+    "Binds": null,
+    "ContainerIDFile": "",
+    "LogConfig": {
+      "Type": "json-file",
+      "Config": {}
+    },
+    "NetworkMode": "default",
+    "PortBindings": {},
+    "RestartPolicy": {
+      "Name": "no",
+      "MaximumRetryCount": 0
+    },
+    "AutoRemove": true,
+    "VolumeDriver": "",
+    "VolumesFrom": null,
+    "CapAdd": null,
+    "CapDrop": null,
+    "CgroupnsMode": "private",
+    "Dns": [],
+    "DnsOptions": [],
+    "DnsSearch": [],
+    "ExtraHosts": null,
+    "GroupAdd": null,
+    "IpcMode": "private",
+    "Cgroup": "",
+    "Links": null,
+    "OomScoreAdj": 0,
+    "PidMode": "",
+    "Privileged": false,
+    "PublishAllPorts": false,
+    "ReadonlyRootfs": false,
+    "SecurityOpt": null,
+    "UTSMode": "",
+    "UsernsMode": "",
+    "ShmSize": 67108864,
+    "Runtime": "runc",
+    "ConsoleSize": [
+      0,
+      0
+    ],
+    "Isolation": "",
+    "CpuShares": 0,
+    "Memory": 0,
+    "NanoCpus": 0,
+    "CgroupParent": "",
+    "BlkioWeight": 0,
+    "BlkioWeightDevice": [],
+    "BlkioDeviceReadBps": null,
+    "BlkioDeviceWriteBps": null,
+    "BlkioDeviceReadIOps": null,
+    "BlkioDeviceWriteIOps": null,
+    "CpuPeriod": 0,
+    "CpuQuota": 0,
+    "CpuRealtimePeriod": 0,
+    "CpuRealtimeRuntime": 0,
+    "CpusetCpus": "",
+    "CpusetMems": "",
+    "Devices": [],
+    "DeviceCgroupRules": null,
+    "DeviceRequests": null,
+    "KernelMemory": 0,
+    "KernelMemoryTCP": 0,
+    "MemoryReservation": 0,
+    "MemorySwap": 0,
+    "MemorySwappiness": null,
+    "OomKillDisable": null,
+    "PidsLimit": null,
+    "Ulimits": null,
+    "CpuCount": 0,
+    "CpuPercent": 0,
+    "IOMaximumIOps": 0,
+    "IOMaximumBandwidth": 0,
+    "MaskedPaths": [
+      "/proc/asound",
+      "/proc/acpi",
+      "/proc/kcore",
+      "/proc/keys",
+      "/proc/latency_stats",
+      "/proc/timer_list",
+      "/proc/timer_stats",
+      "/proc/sched_debug",
+      "/proc/scsi",
+      "/sys/firmware"
+    ],
+    "ReadonlyPaths": [
+      "/proc/bus",
+      "/proc/fs",
+      "/proc/irq",
+      "/proc/sys",
+      "/proc/sysrq-trigger"
+    ]
+  },
+  "GraphDriver": {
+    "Data": {
+      "LowerDir": "/var/lib/docker/overlay2/f2eeb80333f168a9cf70fa660d2995de58b8594b6c2f21ef0b79a1ffb521de66-init/diff:/var/lib/docker/overlay2/8e751899d2413a00b19a7720d29477f48a1d705d3ec8f72b9253e07f8e17ae69/diff:/var/lib/docker/overlay2/0669a25234b52f70f016721a0b59007feae2b11cfb73d9401e72bcaec12cb531/diff:/var/lib/docker/overlay2/6e146df9e4d0b13b226c243082957f38a14aa8b22a35f57d0c6440876ce28f31/diff",
+      "MergedDir": "/var/lib/docker/overlay2/f2eeb80333f168a9cf70fa660d2995de58b8594b6c2f21ef0b79a1ffb521de66/merged",
+      "UpperDir": "/var/lib/docker/overlay2/f2eeb80333f168a9cf70fa660d2995de58b8594b6c2f21ef0b79a1ffb521de66/diff",
+      "WorkDir": "/var/lib/docker/overlay2/f2eeb80333f168a9cf70fa660d2995de58b8594b6c2f21ef0b79a1ffb521de66/work"
+    },
+    "Name": "overlay2"
+  },
+  "Mounts": [],
+  "Config": {
+    "Hostname": "nginx-2.dev",
+    "Domainname": "",
+    "User": "",
+    "AttachStdin": true,
+    "AttachStdout": true,
+    "AttachStderr": true,
+    "ExposedPorts": {
+      "80/tcp": {}
+    },
+    "Tty": true,
+    "OpenStdin": true,
+    "StdinOnce": true,
+    "Env": [
+      "HOSTNAMES=nginx.com.br",
+      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+      "NGINX_VERSION=1.15.12-1~stretch",
+      "NJS_VERSION=1.15.12.0.3.1-1~stretch"
+    ],
+    "Cmd": [
+      "bash"
+    ],
+    "Image": "nginx:1.15",
+    "Volumes": null,
+    "WorkingDir": "",
+    "Entrypoint": null,
+    "OnBuild": null,
+    "Labels": {
+      "dps.network": "shibata",
+      "com.docker.compose.service": "nginx-service",
+      "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
+    },
+    "StopSignal": "SIGTERM"
+  },
+  "NetworkSettings": {
+    "Bridge": "",
+    "SandboxID": "f84ee54b7f81ea1256f5296456dcb223fbfe865d8899c78ef3fa827c6df2751f",
+    "HairpinMode": false,
+    "LinkLocalIPv6Address": "",
+    "LinkLocalIPv6PrefixLen": 0,
+    "Ports": {
+      "80/tcp": null
+    },
+    "SandboxKey": "/var/run/docker/netns/f84ee54b7f81",
+    "SecondaryIPAddresses": null,
+    "SecondaryIPv6Addresses": null,
+    "EndpointID": "dafb3807c71c952fc20c793144a696a76f3ac9d6b7744db043561f480eb4344a",
+    "Gateway": "172.17.0.1",
+    "GlobalIPv6Address": "",
+    "GlobalIPv6PrefixLen": 0,
+    "IPAddress": "172.17.0.4",
+    "IPPrefixLen": 16,
+    "IPv6Gateway": "",
+    "MacAddress": "02:42:ac:11:00:04",
+    "Networks": {
+      "bridge": {
+        "IPAMConfig": null,
+        "Links": null,
+        "Aliases": null,
+        "NetworkID": "a860e3ec98661e614ecdf67b89a8a567c176b634dce98264c32c3837ce017ec2",
+        "EndpointID": "dafb3807c71c952fc20c793144a696a76f3ac9d6b7744db043561f480eb4344a",
+        "Gateway": "172.17.0.1",
+        "IPAddress": "172.17.0.4",
+        "IPPrefixLen": 16,
+        "IPv6Gateway": "",
+        "GlobalIPv6Address": "",
+        "GlobalIPv6PrefixLen": 0,
+        "MacAddress": "02:42:ac:11:00:04",
+        "DriverOpts": null
+      }
+    }
+  }
+}
diff --git a/src/test/resources/templates/nginx-003.json b/src/test/resources/templates/nginx-003.json
new file mode 100644
index 00000000..52f241a2
--- /dev/null
+++ b/src/test/resources/templates/nginx-003.json
@@ -0,0 +1,213 @@
+{
+  "Id": "57e8c8294cfc6d6c650a2192f8195c12b55a41f294be6e2a21599ca248c3181b",
+  "Created": "2023-03-21T04:39:36.180453956Z",
+  "Path": "/docker-entrypoint.sh",
+  "Args": [
+    "nginx",
+    "-g",
+    "daemon off;"
+  ],
+  "State": {
+    "Status": "running",
+    "Running": true,
+    "Paused": false,
+    "Restarting": false,
+    "OOMKilled": false,
+    "Dead": false,
+    "Pid": 59550,
+    "ExitCode": 0,
+    "Error": "",
+    "StartedAt": "2023-03-21T04:39:36.752640082Z",
+    "FinishedAt": "0001-01-01T00:00:00Z"
+  },
+  "Image": "sha256:3f8a00f137a0d2c8a2163a09901e28e2471999fde4efc2f9570b91f1c30acf94",
+  "ResolvConfPath": "/var/lib/docker/containers/57e8c8294cfc6d6c650a2192f8195c12b55a41f294be6e2a21599ca248c3181b/resolv.conf",
+  "HostnamePath": "/var/lib/docker/containers/57e8c8294cfc6d6c650a2192f8195c12b55a41f294be6e2a21599ca248c3181b/hostname",
+  "HostsPath": "/var/lib/docker/containers/57e8c8294cfc6d6c650a2192f8195c12b55a41f294be6e2a21599ca248c3181b/hosts",
+  "LogPath": "/var/lib/docker/containers/57e8c8294cfc6d6c650a2192f8195c12b55a41f294be6e2a21599ca248c3181b/57e8c8294cfc6d6c650a2192f8195c12b55a41f294be6e2a21599ca248c3181b-json.log",
+  "Name": "/nginx1",
+  "RestartCount": 0,
+  "Driver": "overlay2",
+  "Platform": "linux",
+  "MountLabel": "",
+  "ProcessLabel": "",
+  "AppArmorProfile": "docker-default",
+  "ExecIDs": null,
+  "HostConfig": {
+    "Binds": null,
+    "ContainerIDFile": "",
+    "LogConfig": {
+      "Type": "json-file",
+      "Config": {}
+    },
+    "NetworkMode": "default",
+    "PortBindings": {},
+    "RestartPolicy": {
+      "Name": "no",
+      "MaximumRetryCount": 0
+    },
+    "AutoRemove": true,
+    "VolumeDriver": "",
+    "VolumesFrom": null,
+    "CapAdd": null,
+    "CapDrop": null,
+    "CgroupnsMode": "private",
+    "Dns": [],
+    "DnsOptions": [],
+    "DnsSearch": [],
+    "ExtraHosts": null,
+    "GroupAdd": null,
+    "IpcMode": "private",
+    "Cgroup": "",
+    "Links": null,
+    "OomScoreAdj": 0,
+    "PidMode": "",
+    "Privileged": false,
+    "PublishAllPorts": false,
+    "ReadonlyRootfs": false,
+    "SecurityOpt": null,
+    "UTSMode": "",
+    "UsernsMode": "",
+    "ShmSize": 67108864,
+    "Runtime": "runc",
+    "ConsoleSize": [
+      0,
+      0
+    ],
+    "Isolation": "",
+    "CpuShares": 0,
+    "Memory": 0,
+    "NanoCpus": 0,
+    "CgroupParent": "",
+    "BlkioWeight": 0,
+    "BlkioWeightDevice": [],
+    "BlkioDeviceReadBps": null,
+    "BlkioDeviceWriteBps": null,
+    "BlkioDeviceReadIOps": null,
+    "BlkioDeviceWriteIOps": null,
+    "CpuPeriod": 0,
+    "CpuQuota": 0,
+    "CpuRealtimePeriod": 0,
+    "CpuRealtimeRuntime": 0,
+    "CpusetCpus": "",
+    "CpusetMems": "",
+    "Devices": [],
+    "DeviceCgroupRules": null,
+    "DeviceRequests": null,
+    "KernelMemory": 0,
+    "KernelMemoryTCP": 0,
+    "MemoryReservation": 0,
+    "MemorySwap": 0,
+    "MemorySwappiness": null,
+    "OomKillDisable": null,
+    "PidsLimit": null,
+    "Ulimits": null,
+    "CpuCount": 0,
+    "CpuPercent": 0,
+    "IOMaximumIOps": 0,
+    "IOMaximumBandwidth": 0,
+    "MaskedPaths": [
+      "/proc/asound",
+      "/proc/acpi",
+      "/proc/kcore",
+      "/proc/keys",
+      "/proc/latency_stats",
+      "/proc/timer_list",
+      "/proc/timer_stats",
+      "/proc/sched_debug",
+      "/proc/scsi",
+      "/sys/firmware"
+    ],
+    "ReadonlyPaths": [
+      "/proc/bus",
+      "/proc/fs",
+      "/proc/irq",
+      "/proc/sys",
+      "/proc/sysrq-trigger"
+    ]
+  },
+  "GraphDriver": {
+    "Data": {
+      "LowerDir": "/var/lib/docker/overlay2/4103cd7ab8ead6f9a8bae379990e845870076951d65a997d2d7a4b40f3afa5a1-init/diff:/var/lib/docker/overlay2/b042e9d8b874dcc8fa7b6304ea702edc0f6b61de47599652f4ae82a509a6c0af/diff:/var/lib/docker/overlay2/771980f2ec5d14aa4d7214cfcec0b4391ff5976cb03a8e858d9b3b3ac01fbdc8/diff:/var/lib/docker/overlay2/a01c23c69ca2950c210e227cbe2fface05c27933f54aa554745727e0c9dc8317/diff:/var/lib/docker/overlay2/fa7bc5328220e1a6cbe389de44957fa0e6ec4569ad0438cf2189b583c1a700ee/diff:/var/lib/docker/overlay2/59ba752365e357fee176d17531e51bef2a5e19f2348d84e536a693dfb89c1663/diff:/var/lib/docker/overlay2/cb8862cf05fcca99bf2bbbdb0744f193c93b9c06005d3beec1831ae53097f383/diff",
+      "MergedDir": "/var/lib/docker/overlay2/4103cd7ab8ead6f9a8bae379990e845870076951d65a997d2d7a4b40f3afa5a1/merged",
+      "UpperDir": "/var/lib/docker/overlay2/4103cd7ab8ead6f9a8bae379990e845870076951d65a997d2d7a4b40f3afa5a1/diff",
+      "WorkDir": "/var/lib/docker/overlay2/4103cd7ab8ead6f9a8bae379990e845870076951d65a997d2d7a4b40f3afa5a1/work"
+    },
+    "Name": "overlay2"
+  },
+  "Mounts": [],
+  "Config": {
+    "Hostname": "57e8c8294cfc",
+    "Domainname": "",
+    "User": "",
+    "AttachStdin": false,
+    "AttachStdout": true,
+    "AttachStderr": true,
+    "ExposedPorts": {
+      "80/tcp": {}
+    },
+    "Tty": false,
+    "OpenStdin": false,
+    "StdinOnce": false,
+    "Env": [
+      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+      "NGINX_VERSION=1.23.3",
+      "NJS_VERSION=0.7.9",
+      "PKG_RELEASE=1~bullseye"
+    ],
+    "Cmd": [
+      "nginx",
+      "-g",
+      "daemon off;"
+    ],
+    "Image": "nginx",
+    "Volumes": null,
+    "WorkingDir": "",
+    "Entrypoint": [
+      "/docker-entrypoint.sh"
+    ],
+    "OnBuild": null,
+    "Labels": {
+      "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
+    },
+    "StopSignal": "SIGQUIT"
+  },
+  "NetworkSettings": {
+    "Bridge": "",
+    "SandboxID": "9beb467f66ba95bec3c9ad0f65e0c6c439dc95f62bb56779225149eded016136",
+    "HairpinMode": false,
+    "LinkLocalIPv6Address": "",
+    "LinkLocalIPv6PrefixLen": 0,
+    "Ports": {
+      "80/tcp": null
+    },
+    "SandboxKey": "/var/run/docker/netns/9beb467f66ba",
+    "SecondaryIPAddresses": null,
+    "SecondaryIPv6Addresses": null,
+    "EndpointID": "80c26d49d22f971e798c1e63b9fc4f77add0a6777af0591f5abfd55991351ed5",
+    "Gateway": "172.17.0.1",
+    "GlobalIPv6Address": "2001:db8:abc1::242:ac11:4",
+    "GlobalIPv6PrefixLen": 64,
+    "IPAddress": "172.17.0.4",
+    "IPPrefixLen": 16,
+    "IPv6Gateway": "2001:db8:abc1::1",
+    "MacAddress": "02:42:ac:11:00:04",
+    "Networks": {
+      "bridge": {
+        "IPAMConfig": null,
+        "Links": null,
+        "Aliases": null,
+        "NetworkID": "038d0a0f0e8f817bff12dd7b880e8ba808c895ce271754026e06ff88c20f8876",
+        "EndpointID": "80c26d49d22f971e798c1e63b9fc4f77add0a6777af0591f5abfd55991351ed5",
+        "Gateway": "172.17.0.1",
+        "IPAddress": "172.17.0.4",
+        "IPPrefixLen": 16,
+        "IPv6Gateway": "2001:db8:abc1::1",
+        "GlobalIPv6Address": "2001:db8:abc1::242:ac11:4",
+        "GlobalIPv6PrefixLen": 64,
+        "MacAddress": "02:42:ac:11:00:04",
+        "DriverOpts": null
+      }
+    }
+  }
+}
diff --git a/src/test/resources/templates/nginx-004.json b/src/test/resources/templates/nginx-004.json
new file mode 100644
index 00000000..50b0879c
--- /dev/null
+++ b/src/test/resources/templates/nginx-004.json
@@ -0,0 +1,215 @@
+{
+  "Id": "68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a",
+  "Created": "2023-03-21T04:47:04.669430213Z",
+  "Path": "/docker-entrypoint.sh",
+  "Args": [
+    "nginx",
+    "-g",
+    "daemon off;"
+  ],
+  "State": {
+    "Status": "running",
+    "Running": true,
+    "Paused": false,
+    "Restarting": false,
+    "OOMKilled": false,
+    "Dead": false,
+    "Pid": 61116,
+    "ExitCode": 0,
+    "Error": "",
+    "StartedAt": "2023-03-21T04:47:05.280532228Z",
+    "FinishedAt": "0001-01-01T00:00:00Z"
+  },
+  "Image": "sha256:3f8a00f137a0d2c8a2163a09901e28e2471999fde4efc2f9570b91f1c30acf94",
+  "ResolvConfPath": "/var/lib/docker/containers/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a/resolv.conf",
+  "HostnamePath": "/var/lib/docker/containers/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a/hostname",
+  "HostsPath": "/var/lib/docker/containers/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a/hosts",
+  "LogPath": "/var/lib/docker/containers/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a-json.log",
+  "Name": "/nginx1",
+  "RestartCount": 0,
+  "Driver": "overlay2",
+  "Platform": "linux",
+  "MountLabel": "",
+  "ProcessLabel": "",
+  "AppArmorProfile": "docker-default",
+  "ExecIDs": null,
+  "HostConfig": {
+    "Binds": null,
+    "ContainerIDFile": "",
+    "LogConfig": {
+      "Type": "json-file",
+      "Config": {}
+    },
+    "NetworkMode": "my-net1",
+    "PortBindings": {},
+    "RestartPolicy": {
+      "Name": "no",
+      "MaximumRetryCount": 0
+    },
+    "AutoRemove": true,
+    "VolumeDriver": "",
+    "VolumesFrom": null,
+    "CapAdd": null,
+    "CapDrop": null,
+    "CgroupnsMode": "private",
+    "Dns": [],
+    "DnsOptions": [],
+    "DnsSearch": [],
+    "ExtraHosts": null,
+    "GroupAdd": null,
+    "IpcMode": "private",
+    "Cgroup": "",
+    "Links": null,
+    "OomScoreAdj": 0,
+    "PidMode": "",
+    "Privileged": false,
+    "PublishAllPorts": false,
+    "ReadonlyRootfs": false,
+    "SecurityOpt": null,
+    "UTSMode": "",
+    "UsernsMode": "",
+    "ShmSize": 67108864,
+    "Runtime": "runc",
+    "ConsoleSize": [
+      0,
+      0
+    ],
+    "Isolation": "",
+    "CpuShares": 0,
+    "Memory": 0,
+    "NanoCpus": 0,
+    "CgroupParent": "",
+    "BlkioWeight": 0,
+    "BlkioWeightDevice": [],
+    "BlkioDeviceReadBps": null,
+    "BlkioDeviceWriteBps": null,
+    "BlkioDeviceReadIOps": null,
+    "BlkioDeviceWriteIOps": null,
+    "CpuPeriod": 0,
+    "CpuQuota": 0,
+    "CpuRealtimePeriod": 0,
+    "CpuRealtimeRuntime": 0,
+    "CpusetCpus": "",
+    "CpusetMems": "",
+    "Devices": [],
+    "DeviceCgroupRules": null,
+    "DeviceRequests": null,
+    "KernelMemory": 0,
+    "KernelMemoryTCP": 0,
+    "MemoryReservation": 0,
+    "MemorySwap": 0,
+    "MemorySwappiness": null,
+    "OomKillDisable": null,
+    "PidsLimit": null,
+    "Ulimits": null,
+    "CpuCount": 0,
+    "CpuPercent": 0,
+    "IOMaximumIOps": 0,
+    "IOMaximumBandwidth": 0,
+    "MaskedPaths": [
+      "/proc/asound",
+      "/proc/acpi",
+      "/proc/kcore",
+      "/proc/keys",
+      "/proc/latency_stats",
+      "/proc/timer_list",
+      "/proc/timer_stats",
+      "/proc/sched_debug",
+      "/proc/scsi",
+      "/sys/firmware"
+    ],
+    "ReadonlyPaths": [
+      "/proc/bus",
+      "/proc/fs",
+      "/proc/irq",
+      "/proc/sys",
+      "/proc/sysrq-trigger"
+    ]
+  },
+  "GraphDriver": {
+    "Data": {
+      "LowerDir": "/var/lib/docker/overlay2/021ef333425f3b618ff38ae9fbc729cd7e3e716d5a7c5340a848b299d2d4c5cf-init/diff:/var/lib/docker/overlay2/b042e9d8b874dcc8fa7b6304ea702edc0f6b61de47599652f4ae82a509a6c0af/diff:/var/lib/docker/overlay2/771980f2ec5d14aa4d7214cfcec0b4391ff5976cb03a8e858d9b3b3ac01fbdc8/diff:/var/lib/docker/overlay2/a01c23c69ca2950c210e227cbe2fface05c27933f54aa554745727e0c9dc8317/diff:/var/lib/docker/overlay2/fa7bc5328220e1a6cbe389de44957fa0e6ec4569ad0438cf2189b583c1a700ee/diff:/var/lib/docker/overlay2/59ba752365e357fee176d17531e51bef2a5e19f2348d84e536a693dfb89c1663/diff:/var/lib/docker/overlay2/cb8862cf05fcca99bf2bbbdb0744f193c93b9c06005d3beec1831ae53097f383/diff",
+      "MergedDir": "/var/lib/docker/overlay2/021ef333425f3b618ff38ae9fbc729cd7e3e716d5a7c5340a848b299d2d4c5cf/merged",
+      "UpperDir": "/var/lib/docker/overlay2/021ef333425f3b618ff38ae9fbc729cd7e3e716d5a7c5340a848b299d2d4c5cf/diff",
+      "WorkDir": "/var/lib/docker/overlay2/021ef333425f3b618ff38ae9fbc729cd7e3e716d5a7c5340a848b299d2d4c5cf/work"
+    },
+    "Name": "overlay2"
+  },
+  "Mounts": [],
+  "Config": {
+    "Hostname": "68e47d6c42ab",
+    "Domainname": "",
+    "User": "",
+    "AttachStdin": false,
+    "AttachStdout": true,
+    "AttachStderr": true,
+    "ExposedPorts": {
+      "80/tcp": {}
+    },
+    "Tty": false,
+    "OpenStdin": false,
+    "StdinOnce": false,
+    "Env": [
+      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+      "NGINX_VERSION=1.23.3",
+      "NJS_VERSION=0.7.9",
+      "PKG_RELEASE=1~bullseye"
+    ],
+    "Cmd": [
+      "nginx",
+      "-g",
+      "daemon off;"
+    ],
+    "Image": "nginx",
+    "Volumes": null,
+    "WorkingDir": "",
+    "Entrypoint": [
+      "/docker-entrypoint.sh"
+    ],
+    "OnBuild": null,
+    "Labels": {
+      "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
+    },
+    "StopSignal": "SIGQUIT"
+  },
+  "NetworkSettings": {
+    "Bridge": "",
+    "SandboxID": "504ec5176af5dd6eb567b6403486cefaecba28315d7893062337c9894a1cf275",
+    "HairpinMode": false,
+    "LinkLocalIPv6Address": "",
+    "LinkLocalIPv6PrefixLen": 0,
+    "Ports": {
+      "80/tcp": null
+    },
+    "SandboxKey": "/var/run/docker/netns/504ec5176af5",
+    "SecondaryIPAddresses": null,
+    "SecondaryIPv6Addresses": null,
+    "EndpointID": "",
+    "Gateway": "",
+    "GlobalIPv6Address": "",
+    "GlobalIPv6PrefixLen": 0,
+    "IPAddress": "",
+    "IPPrefixLen": 0,
+    "IPv6Gateway": "",
+    "MacAddress": "",
+    "Networks": {
+      "my-net1": {
+        "IPAMConfig": null,
+        "Links": null,
+        "Aliases": [
+          "68e47d6c42ab"
+        ],
+        "NetworkID": "0ec5522fa0b270d6d4727065a65668661b5c116d77542335e2a4497994935e05",
+        "EndpointID": "4d398f450b14c320c25f457f32c140131f5ec5bb55b6300bd261909d2f8e6aba",
+        "Gateway": "172.21.0.1",
+        "IPAddress": "172.21.0.2",
+        "IPPrefixLen": 16,
+        "IPv6Gateway": "2001:db8:1::1",
+        "GlobalIPv6Address": "2001:db8:1::2",
+        "GlobalIPv6PrefixLen": 64,
+        "MacAddress": "02:42:ac:15:00:02",
+        "DriverOpts": null
+      }
+    }
+  }
+}
diff --git a/src/test/resources/templates/nginx-005.json b/src/test/resources/templates/nginx-005.json
new file mode 100644
index 00000000..451c1218
--- /dev/null
+++ b/src/test/resources/templates/nginx-005.json
@@ -0,0 +1,215 @@
+{
+  "Id": "68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a",
+  "Created": "2023-03-21T04:47:04.669430213Z",
+  "Path": "/docker-entrypoint.sh",
+  "Args": [
+    "nginx",
+    "-g",
+    "daemon off;"
+  ],
+  "State": {
+    "Status": "running",
+    "Running": true,
+    "Paused": false,
+    "Restarting": false,
+    "OOMKilled": false,
+    "Dead": false,
+    "Pid": 61116,
+    "ExitCode": 0,
+    "Error": "",
+    "StartedAt": "2023-03-21T04:47:05.280532228Z",
+    "FinishedAt": "0001-01-01T00:00:00Z"
+  },
+  "Image": "sha256:3f8a00f137a0d2c8a2163a09901e28e2471999fde4efc2f9570b91f1c30acf94",
+  "ResolvConfPath": "/var/lib/docker/containers/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a/resolv.conf",
+  "HostnamePath": "/var/lib/docker/containers/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a/hostname",
+  "HostsPath": "/var/lib/docker/containers/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a/hosts",
+  "LogPath": "/var/lib/docker/containers/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a-json.log",
+  "Name": "/nginx1",
+  "RestartCount": 0,
+  "Driver": "overlay2",
+  "Platform": "linux",
+  "MountLabel": "",
+  "ProcessLabel": "",
+  "AppArmorProfile": "docker-default",
+  "ExecIDs": null,
+  "HostConfig": {
+    "Binds": null,
+    "ContainerIDFile": "",
+    "LogConfig": {
+      "Type": "json-file",
+      "Config": {}
+    },
+    "NetworkMode": "my-net1",
+    "PortBindings": {},
+    "RestartPolicy": {
+      "Name": "no",
+      "MaximumRetryCount": 0
+    },
+    "AutoRemove": true,
+    "VolumeDriver": "",
+    "VolumesFrom": null,
+    "CapAdd": null,
+    "CapDrop": null,
+    "CgroupnsMode": "private",
+    "Dns": [],
+    "DnsOptions": [],
+    "DnsSearch": [],
+    "ExtraHosts": null,
+    "GroupAdd": null,
+    "IpcMode": "private",
+    "Cgroup": "",
+    "Links": null,
+    "OomScoreAdj": 0,
+    "PidMode": "",
+    "Privileged": false,
+    "PublishAllPorts": false,
+    "ReadonlyRootfs": false,
+    "SecurityOpt": null,
+    "UTSMode": "",
+    "UsernsMode": "",
+    "ShmSize": 67108864,
+    "Runtime": "runc",
+    "ConsoleSize": [
+      0,
+      0
+    ],
+    "Isolation": "",
+    "CpuShares": 0,
+    "Memory": 0,
+    "NanoCpus": 0,
+    "CgroupParent": "",
+    "BlkioWeight": 0,
+    "BlkioWeightDevice": [],
+    "BlkioDeviceReadBps": null,
+    "BlkioDeviceWriteBps": null,
+    "BlkioDeviceReadIOps": null,
+    "BlkioDeviceWriteIOps": null,
+    "CpuPeriod": 0,
+    "CpuQuota": 0,
+    "CpuRealtimePeriod": 0,
+    "CpuRealtimeRuntime": 0,
+    "CpusetCpus": "",
+    "CpusetMems": "",
+    "Devices": [],
+    "DeviceCgroupRules": null,
+    "DeviceRequests": null,
+    "KernelMemory": 0,
+    "KernelMemoryTCP": 0,
+    "MemoryReservation": 0,
+    "MemorySwap": 0,
+    "MemorySwappiness": null,
+    "OomKillDisable": null,
+    "PidsLimit": null,
+    "Ulimits": null,
+    "CpuCount": 0,
+    "CpuPercent": 0,
+    "IOMaximumIOps": 0,
+    "IOMaximumBandwidth": 0,
+    "MaskedPaths": [
+      "/proc/asound",
+      "/proc/acpi",
+      "/proc/kcore",
+      "/proc/keys",
+      "/proc/latency_stats",
+      "/proc/timer_list",
+      "/proc/timer_stats",
+      "/proc/sched_debug",
+      "/proc/scsi",
+      "/sys/firmware"
+    ],
+    "ReadonlyPaths": [
+      "/proc/bus",
+      "/proc/fs",
+      "/proc/irq",
+      "/proc/sys",
+      "/proc/sysrq-trigger"
+    ]
+  },
+  "GraphDriver": {
+    "Data": {
+      "LowerDir": "/var/lib/docker/overlay2/021ef333425f3b618ff38ae9fbc729cd7e3e716d5a7c5340a848b299d2d4c5cf-init/diff:/var/lib/docker/overlay2/b042e9d8b874dcc8fa7b6304ea702edc0f6b61de47599652f4ae82a509a6c0af/diff:/var/lib/docker/overlay2/771980f2ec5d14aa4d7214cfcec0b4391ff5976cb03a8e858d9b3b3ac01fbdc8/diff:/var/lib/docker/overlay2/a01c23c69ca2950c210e227cbe2fface05c27933f54aa554745727e0c9dc8317/diff:/var/lib/docker/overlay2/fa7bc5328220e1a6cbe389de44957fa0e6ec4569ad0438cf2189b583c1a700ee/diff:/var/lib/docker/overlay2/59ba752365e357fee176d17531e51bef2a5e19f2348d84e536a693dfb89c1663/diff:/var/lib/docker/overlay2/cb8862cf05fcca99bf2bbbdb0744f193c93b9c06005d3beec1831ae53097f383/diff",
+      "MergedDir": "/var/lib/docker/overlay2/021ef333425f3b618ff38ae9fbc729cd7e3e716d5a7c5340a848b299d2d4c5cf/merged",
+      "UpperDir": "/var/lib/docker/overlay2/021ef333425f3b618ff38ae9fbc729cd7e3e716d5a7c5340a848b299d2d4c5cf/diff",
+      "WorkDir": "/var/lib/docker/overlay2/021ef333425f3b618ff38ae9fbc729cd7e3e716d5a7c5340a848b299d2d4c5cf/work"
+    },
+    "Name": "overlay2"
+  },
+  "Mounts": [],
+  "Config": {
+    "Hostname": "68e47d6c42ab",
+    "Domainname": "",
+    "User": "",
+    "AttachStdin": false,
+    "AttachStdout": true,
+    "AttachStderr": true,
+    "ExposedPorts": {
+      "80/tcp": {}
+    },
+    "Tty": false,
+    "OpenStdin": false,
+    "StdinOnce": false,
+    "Env": [
+      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+      "NGINX_VERSION=1.23.3",
+      "NJS_VERSION=0.7.9",
+      "PKG_RELEASE=1~bullseye"
+    ],
+    "Cmd": [
+      "nginx",
+      "-g",
+      "daemon off;"
+    ],
+    "Image": "nginx",
+    "Volumes": null,
+    "WorkingDir": "",
+    "Entrypoint": [
+      "/docker-entrypoint.sh"
+    ],
+    "OnBuild": null,
+    "Labels": {
+      "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
+    },
+    "StopSignal": "SIGQUIT"
+  },
+  "NetworkSettings": {
+    "Bridge": "",
+    "SandboxID": "504ec5176af5dd6eb567b6403486cefaecba28315d7893062337c9894a1cf275",
+    "HairpinMode": false,
+    "LinkLocalIPv6Address": "",
+    "LinkLocalIPv6PrefixLen": 0,
+    "Ports": {
+      "80/tcp": null
+    },
+    "SandboxKey": "/var/run/docker/netns/504ec5176af5",
+    "SecondaryIPAddresses": null,
+    "SecondaryIPv6Addresses": null,
+    "EndpointID": "",
+    "Gateway": "",
+    "GlobalIPv6Address": "2001:db7:1::2",
+    "GlobalIPv6PrefixLen": 0,
+    "IPAddress": "",
+    "IPPrefixLen": 0,
+    "IPv6Gateway": "",
+    "MacAddress": "",
+    "Networks": {
+      "my-net1": {
+        "IPAMConfig": null,
+        "Links": null,
+        "Aliases": [
+          "68e47d6c42ab"
+        ],
+        "NetworkID": "0ec5522fa0b270d6d4727065a65668661b5c116d77542335e2a4497994935e05",
+        "EndpointID": "4d398f450b14c320c25f457f32c140131f5ec5bb55b6300bd261909d2f8e6aba",
+        "Gateway": "172.21.0.1",
+        "IPAddress": "172.21.0.2",
+        "IPPrefixLen": 16,
+        "IPv6Gateway": "",
+        "GlobalIPv6Address": "",
+        "GlobalIPv6PrefixLen": 0,
+        "MacAddress": "02:42:ac:15:00:02",
+        "DriverOpts": null
+      }
+    }
+  }
+}
diff --git a/src/test/resources/templates/nginx-006.json b/src/test/resources/templates/nginx-006.json
new file mode 100644
index 00000000..bf32ddf5
--- /dev/null
+++ b/src/test/resources/templates/nginx-006.json
@@ -0,0 +1,230 @@
+{
+  "Id": "68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a",
+  "Created": "2023-03-21T04:47:04.669430213Z",
+  "Path": "/docker-entrypoint.sh",
+  "Args": [
+    "nginx",
+    "-g",
+    "daemon off;"
+  ],
+  "State": {
+    "Status": "running",
+    "Running": true,
+    "Paused": false,
+    "Restarting": false,
+    "OOMKilled": false,
+    "Dead": false,
+    "Pid": 61116,
+    "ExitCode": 0,
+    "Error": "",
+    "StartedAt": "2023-03-21T04:47:05.280532228Z",
+    "FinishedAt": "0001-01-01T00:00:00Z"
+  },
+  "Image": "sha256:3f8a00f137a0d2c8a2163a09901e28e2471999fde4efc2f9570b91f1c30acf94",
+  "ResolvConfPath": "/var/lib/docker/containers/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a/resolv.conf",
+  "HostnamePath": "/var/lib/docker/containers/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a/hostname",
+  "HostsPath": "/var/lib/docker/containers/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a/hosts",
+  "LogPath": "/var/lib/docker/containers/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a/68e47d6c42ab19b62bd57a57a93247537872513a64e03f31b7ad994e9e9cf23a-json.log",
+  "Name": "/nginx1",
+  "RestartCount": 0,
+  "Driver": "overlay2",
+  "Platform": "linux",
+  "MountLabel": "",
+  "ProcessLabel": "",
+  "AppArmorProfile": "docker-default",
+  "ExecIDs": null,
+  "HostConfig": {
+    "Binds": null,
+    "ContainerIDFile": "",
+    "LogConfig": {
+      "Type": "json-file",
+      "Config": {}
+    },
+    "NetworkMode": "my-net1",
+    "PortBindings": {},
+    "RestartPolicy": {
+      "Name": "no",
+      "MaximumRetryCount": 0
+    },
+    "AutoRemove": true,
+    "VolumeDriver": "",
+    "VolumesFrom": null,
+    "CapAdd": null,
+    "CapDrop": null,
+    "CgroupnsMode": "private",
+    "Dns": [],
+    "DnsOptions": [],
+    "DnsSearch": [],
+    "ExtraHosts": null,
+    "GroupAdd": null,
+    "IpcMode": "private",
+    "Cgroup": "",
+    "Links": null,
+    "OomScoreAdj": 0,
+    "PidMode": "",
+    "Privileged": false,
+    "PublishAllPorts": false,
+    "ReadonlyRootfs": false,
+    "SecurityOpt": null,
+    "UTSMode": "",
+    "UsernsMode": "",
+    "ShmSize": 67108864,
+    "Runtime": "runc",
+    "ConsoleSize": [
+      0,
+      0
+    ],
+    "Isolation": "",
+    "CpuShares": 0,
+    "Memory": 0,
+    "NanoCpus": 0,
+    "CgroupParent": "",
+    "BlkioWeight": 0,
+    "BlkioWeightDevice": [],
+    "BlkioDeviceReadBps": null,
+    "BlkioDeviceWriteBps": null,
+    "BlkioDeviceReadIOps": null,
+    "BlkioDeviceWriteIOps": null,
+    "CpuPeriod": 0,
+    "CpuQuota": 0,
+    "CpuRealtimePeriod": 0,
+    "CpuRealtimeRuntime": 0,
+    "CpusetCpus": "",
+    "CpusetMems": "",
+    "Devices": [],
+    "DeviceCgroupRules": null,
+    "DeviceRequests": null,
+    "KernelMemory": 0,
+    "KernelMemoryTCP": 0,
+    "MemoryReservation": 0,
+    "MemorySwap": 0,
+    "MemorySwappiness": null,
+    "OomKillDisable": null,
+    "PidsLimit": null,
+    "Ulimits": null,
+    "CpuCount": 0,
+    "CpuPercent": 0,
+    "IOMaximumIOps": 0,
+    "IOMaximumBandwidth": 0,
+    "MaskedPaths": [
+      "/proc/asound",
+      "/proc/acpi",
+      "/proc/kcore",
+      "/proc/keys",
+      "/proc/latency_stats",
+      "/proc/timer_list",
+      "/proc/timer_stats",
+      "/proc/sched_debug",
+      "/proc/scsi",
+      "/sys/firmware"
+    ],
+    "ReadonlyPaths": [
+      "/proc/bus",
+      "/proc/fs",
+      "/proc/irq",
+      "/proc/sys",
+      "/proc/sysrq-trigger"
+    ]
+  },
+  "GraphDriver": {
+    "Data": {
+      "LowerDir": "/var/lib/docker/overlay2/021ef333425f3b618ff38ae9fbc729cd7e3e716d5a7c5340a848b299d2d4c5cf-init/diff:/var/lib/docker/overlay2/b042e9d8b874dcc8fa7b6304ea702edc0f6b61de47599652f4ae82a509a6c0af/diff:/var/lib/docker/overlay2/771980f2ec5d14aa4d7214cfcec0b4391ff5976cb03a8e858d9b3b3ac01fbdc8/diff:/var/lib/docker/overlay2/a01c23c69ca2950c210e227cbe2fface05c27933f54aa554745727e0c9dc8317/diff:/var/lib/docker/overlay2/fa7bc5328220e1a6cbe389de44957fa0e6ec4569ad0438cf2189b583c1a700ee/diff:/var/lib/docker/overlay2/59ba752365e357fee176d17531e51bef2a5e19f2348d84e536a693dfb89c1663/diff:/var/lib/docker/overlay2/cb8862cf05fcca99bf2bbbdb0744f193c93b9c06005d3beec1831ae53097f383/diff",
+      "MergedDir": "/var/lib/docker/overlay2/021ef333425f3b618ff38ae9fbc729cd7e3e716d5a7c5340a848b299d2d4c5cf/merged",
+      "UpperDir": "/var/lib/docker/overlay2/021ef333425f3b618ff38ae9fbc729cd7e3e716d5a7c5340a848b299d2d4c5cf/diff",
+      "WorkDir": "/var/lib/docker/overlay2/021ef333425f3b618ff38ae9fbc729cd7e3e716d5a7c5340a848b299d2d4c5cf/work"
+    },
+    "Name": "overlay2"
+  },
+  "Mounts": [],
+  "Config": {
+    "Hostname": "68e47d6c42ab",
+    "Domainname": "",
+    "User": "",
+    "AttachStdin": false,
+    "AttachStdout": true,
+    "AttachStderr": true,
+    "ExposedPorts": {
+      "80/tcp": {}
+    },
+    "Tty": false,
+    "OpenStdin": false,
+    "StdinOnce": false,
+    "Env": [
+      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+      "NGINX_VERSION=1.23.3",
+      "NJS_VERSION=0.7.9",
+      "PKG_RELEASE=1~bullseye"
+    ],
+    "Cmd": [
+      "nginx",
+      "-g",
+      "daemon off;"
+    ],
+    "Image": "nginx",
+    "Volumes": null,
+    "WorkingDir": "",
+    "Entrypoint": [
+      "/docker-entrypoint.sh"
+    ],
+    "OnBuild": null,
+    "Labels": {
+      "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
+    },
+    "StopSignal": "SIGQUIT"
+  },
+  "NetworkSettings": {
+    "Bridge": "",
+    "SandboxID": "504ec5176af5dd6eb567b6403486cefaecba28315d7893062337c9894a1cf275",
+    "HairpinMode": false,
+    "LinkLocalIPv6Address": "",
+    "LinkLocalIPv6PrefixLen": 0,
+    "Ports": {
+      "80/tcp": null
+    },
+    "SandboxKey": "/var/run/docker/netns/504ec5176af5",
+    "SecondaryIPAddresses": null,
+    "SecondaryIPv6Addresses": null,
+    "EndpointID": "",
+    "Gateway": "",
+    "GlobalIPv6Address": "",
+    "GlobalIPv6PrefixLen": 0,
+    "IPAddress": "",
+    "IPPrefixLen": 0,
+    "IPv6Gateway": "",
+    "MacAddress": "",
+    "Networks": {
+      "bridge": {
+        "IPAMConfig": null,
+        "Links": null,
+        "Aliases": null,
+        "NetworkID": "a860e3ec98661e614ecdf67b89a8a567c176b634dce98264c32c3837ce017ec2",
+        "EndpointID": "dafb3807c71c952fc20c793144a696a76f3ac9d6b7744db043561f480eb4344a",
+        "Gateway": "172.17.0.1",
+        "IPAddress": "172.17.0.4",
+        "IPPrefixLen": 16,
+        "IPv6Gateway": "",
+        "GlobalIPv6Address": "",
+        "GlobalIPv6PrefixLen": 0,
+        "MacAddress": "02:42:ac:11:00:04",
+        "DriverOpts": null
+      },
+      "my-net1": {
+        "IPAMConfig": null,
+        "Links": null,
+        "Aliases": [
+          "68e47d6c42ab"
+        ],
+        "NetworkID": "0ec5522fa0b270d6d4727065a65668661b5c116d77542335e2a4497994935e05",
+        "EndpointID": "4d398f450b14c320c25f457f32c140131f5ec5bb55b6300bd261909d2f8e6aba",
+        "Gateway": "172.21.0.1",
+        "IPAddress": "172.21.0.2",
+        "IPPrefixLen": 16,
+        "IPv6Gateway": "2001:db8:1::1",
+        "GlobalIPv6Address": "2001:db8:1::2",
+        "GlobalIPv6PrefixLen": 64,
+        "MacAddress": "02:42:ac:15:00:02",
+        "DriverOpts": null
+      }
+    }
+  }
+}