diff --git a/Traefik.php b/Traefik.php
index 63a7df3079c11e355aa34c5305bbfb03bb1b7f3f..f7090672bbefeb12f5f5fee14e01c36085473cf3 100644
--- a/Traefik.php
+++ b/Traefik.php
@@ -17,42 +17,67 @@ class Traefik {
    *
    * @var string
    */
-  protected $name;
+  protected string $name;
 
   /**
    * @var string
    */
-  protected $domain;
+  protected string $domain;
 
   /**
    * @var int
    */
-  protected $http_port;
+  protected int $http_port;
 
   /**
    * @var int
    */
-  protected $https_port;
+  protected int $https_port;
 
   /**
    * @var string
    */
-  protected $cert_filename;
+  protected string $cert_filename;
 
   /**
    * @var string
    */
-  protected $key_filename;
+  protected string $key_filename;
 
   /**
    * @var bool
    */
-  protected $addon_portainer = FALSE;
+  protected bool $addon_portainer = FALSE;
 
   /**
    * @var string
    */
-  protected $hub_token = '';
+  protected string $hub_token = '';
+
+  /**
+   * @var array
+   */
+  protected array $env;
+
+  /**
+   * @var bool
+   */
+  protected bool $tls;
+
+  /**
+   * @var bool
+   */
+  protected bool $dns_challenge;
+
+  /**
+   * @var string
+   */
+  protected string $dns_challenge_provider;
+
+  /**
+   * @var string
+   */
+  protected string $dns_challenge_resolver;
 
   /**
    * Traefik constructor.
@@ -70,14 +95,45 @@ class Traefik {
    *   Filename of the SSL certificate.
    * @param string $key_filename
    *   Filename of the private key for the SSL certificate.
+   * @param array $env
+   *   A list of environment variables for the Traefik container.
+   * @param bool $tls
+   *   Whether TLS should be supported.
+   * @param bool $dns_challenge
+   *   If TLS is supported, it uses the http challenge by default. Set to TRUE
+   *   to use the DNS challenge.
+   * @param string $dns_challenge_provider
+   *   If DNS challenge should be used, a provider is required. For a list of
+   *   supported providers:
+   *   @see https://doc.traefik.io/traefik/https/acme/#providers
+   * @param string $dns_challenge_resolver
+   *   In some scenarios, the DNS resolver needs to be defined in order to
+   *   prevent local or shadow DNS servers being used.
    */
-  public function __construct(string $name, $domain = 'docker.localhost', $http_port = 8000, $https_port = 8443, $cert_filename = 'fullchain.pem', $key_filename = 'privkey.pem') {
+  public function __construct(
+    string $name,
+    string $domain = 'docker.localhost',
+    int $http_port = 8000,
+    int $https_port = 8443,
+    string $cert_filename = '',
+    string $key_filename = '',
+    array $env = [],
+    bool $tls = FALSE,
+    bool $dns_challenge = FALSE,
+    string $dns_challenge_provider = '',
+    string $dns_challenge_resolver = ''
+  ) {
     $this->name = $name;
     $this->domain = $domain;
     $this->http_port = $http_port;
     $this->https_port = $https_port;
     $this->cert_filename = $cert_filename;
     $this->key_filename = $key_filename;
+    $this->env = $env;
+    $this->tls = $tls;
+    $this->dns_challenge = $dns_challenge;
+    $this->dns_challenge_provider = $dns_challenge_provider;
+    $this->dns_challenge_resolver = $dns_challenge_resolver;
   }
 
   /**
@@ -146,6 +202,13 @@ class Traefik {
    * @return bool
    */
   private function updateFile(string $filename, string $content): bool {
+    if ($content === '') {
+      if (file_exists($filename)) {
+        unlink($filename);
+        return TRUE;
+      }
+      return FALSE;
+    }
     if (file_exists($filename) && file_get_contents($filename) === $content) {
       return FALSE;
     }
@@ -162,15 +225,13 @@ class Traefik {
       'services' => [
         'traefik' => [
           'container_name' => 'traefik',
-          'image' => 'traefik:2.7',
+          'image' => 'traefik:2.9',
           'command' => [
             '--api=true',
             '--api.dashboard=true',
             '--api.insecure=true',
+            '--log.level=warning',
             '--entrypoints.web.address=:80',
-            '--entrypoints.websecure.address=:443',
-            '--entrypoints.websecure.http.tls.domains[0].main=' . $this->domain,
-            '--entrypoints.websecure.http.tls.domains[0].sans=.' . $this->domain,
             '--providers.file.directory=/configuration/',
             '--providers.file.watch=true',
             '--providers.docker=true',
@@ -182,7 +243,6 @@ class Traefik {
           ],
           'ports' => [
             $this->http_port . ':80',
-            $this->https_port . ':443',
           ],
           'labels' => [
             'traefik.enable=true',
@@ -203,10 +263,13 @@ class Traefik {
         ],
       ],
     ];
+    if (!empty($this->env)) {
+      $config['services']['traefik']['environment'] = $this->env;
+    }
     if ($this->addon_portainer) {
       $config['services']['portainer'] = [
         'container_name' => 'portainer',
-        'image' => 'portainer/portainer-ce:2.11.1',
+        'image' => 'portainer/portainer-ce:2.17.1',
         'command' => '-H unix:///var/run/docker.sock',
         'restart' => 'unless-stopped',
         'networks' => [
@@ -238,7 +301,7 @@ class Traefik {
       $config['services']['traefik']['ports'][] = '9901:9901';
       $config['services']['hub-agent'] = [
         'container_name' => 'hub-agent',
-        'image' => 'ghcr.io/traefik/hub-agent-traefik:v0.7.0',
+        'image' => 'ghcr.io/traefik/hub-agent-traefik:v1.0.1',
         'command' => [
           'run',
           '--hub.token=' . $this->hub_token,
@@ -258,6 +321,33 @@ class Traefik {
         ],
       ];
     }
+    if ($this->tls) {
+      $config['services']['traefik']['command'][] = '--entrypoints.websecure.address=:443';
+      $config['services']['traefik']['command'][] = '--entrypoints.web.http.redirections.entrypoint.to=websecure';
+      $config['services']['traefik']['command'][] = '--certificatesresolvers.lakedrops.acme.email=admin@' . $this->domain;
+      $config['services']['traefik']['ports'][] = $this->https_port . ':443';
+      $config['services']['traefik']['labels'][] = 'traefik.http.routers.traefik.tls=true';
+      $config['services']['traefik']['labels'][] = 'traefik.http.routers.traefik.tls.certresolver=lakedrops';
+      if ($this->addon_portainer) {
+        $config['services']['portainer']['labels'][] = 'traefik.http.routers.traefik.tls=true';
+        $config['services']['portainer']['labels'][] = 'traefik.http.routers.traefik.tls.certresolver=lakedrops';
+      }
+      if ($this->hub_token) {
+        $config['services']['hub-agent']['labels'][] = 'traefik.http.routers.traefik.tls=true';
+        $config['services']['hub-agent']['labels'][] = 'traefik.http.routers.traefik.tls.certresolver=lakedrops';
+      }
+      if ($this->dns_challenge && $this->dns_challenge_provider !== '') {
+        $config['services']['traefik']['command'][] = '--certificatesresolvers.lakedrops.acme.dnschallenge=true';
+        $config['services']['traefik']['command'][] = '--certificatesresolvers.lakedrops.acme.dnschallenge.provider=' . $this->dns_challenge_provider;
+        if ($this->dns_challenge_resolver !== '') {
+          $config['services']['traefik']['command'][] = '--certificatesresolvers.lakedrops.acme.dnschallenge.resolvers=' . $this->dns_challenge_resolver;
+        }
+      }
+      else {
+        $config['services']['traefik']['command'][] = '--certificatesresolvers.lakedrops.acme.storage=/etc/traefik/acme/acme.json';
+        $config['services']['traefik']['command'][] = '--certificatesresolvers.lakedrops.acme.httpchallenge.entrypoint=web';
+      }
+    }
     return $config;
   }
 
@@ -265,6 +355,9 @@ class Traefik {
    * @return string
    */
   private function defaultCertificatesConfig(): string {
+    if ($this->cert_filename === '' || $this->key_filename === '') {
+      return '';
+    }
     return <<<EOF
 [[tls.certificates]]
    certFile = "/certs/$this->cert_filename"