diff --git a/README.md b/README.md
index db51b018ae441cd282c16481eaef5e78784b6bee..2d91e957d95e9ce876b7e539139da878c7f7c803 100644
--- a/README.md
+++ b/README.md
@@ -7,14 +7,26 @@ Role Variables
 --------------
 
 ```yaml
+---
 # certbot settings
+certbot_dns_challenge: false                                                          # default use webserver, true to obtain certificate using dns challenge
+certbot_lego_version: "4.11.0"                                                        # version of LEGO client
+certbot_dns_provider: "designate"                                                     # list of available providers https://go-acme.github.io/lego/dns/
+dns_provider_auth_env_variables:                                                      # variables required to authenticate dns provider
+  OS_AUTH_URL: "https://openstack.example.org"
+  OS_REGION_NAME: "RegionOne"
+  OS_AUTH_TYP: "v3applicationcredential"                                              # default is to use application credential rather than password
+  OS_APPLICATION_CREDENTIAL_ID: "{{ vault_os_application_credential_id }}"
+  OS_APPLICATION_CREDENTIAL_SECRET: "{{ vault_os_application_credential_secret }}"
+
+
 certbot_fqdn:
   - example.de
 certbot_admin_email: "admin@example.de"
-# certbot_webroot: "/var/www/example"                          # if undefined use --standalone
+# certbot_webroot: "/var/www/example"                                                 # if undefined use --standalone
 
 # application settings
-# certbot_application: "example"                               # if defined copy certs to application dir and setup deploy hooks
+# certbot_application: "example"                                                      # if defined copy certs to application dir and setup deploy hooks
 certbot_application_dir: "/etc/{{ certbot_application }}"
 certbot_application_deploy_hook: |
   #!/bin/sh
diff --git a/defaults/main.yml b/defaults/main.yml
index 0daf2e52cdb1617acb59f0d7bd6062fc84aa1243..43d75cc8713c356bc6ac167c2017eeb40f026d8e 100644
--- a/defaults/main.yml
+++ b/defaults/main.yml
@@ -1,12 +1,23 @@
 ---
 # certbot settings
+certbot_dns_challenge: false                                                          # default use webserver, true to obtain certificate using dns challenge
+certbot_lego_version: "4.11.0"                                                        # lego version - Let's Encrypt client
+certbot_dns_provider: "designate"                                                     # list of available providers https://go-acme.github.io/lego/dns/
+dns_provider_auth_env_variables:                                                      # variables required to authenticate dns provider
+  OS_AUTH_URL: "https://openstack.example.org"
+  OS_REGION_NAME: "RegionOne"
+  OS_AUTH_TYP: "v3applicationcredential"                                              # default is to use application credential rather than password
+  OS_APPLICATION_CREDENTIAL_ID: "{{ vault_os_application_credential_id }}"
+  OS_APPLICATION_CREDENTIAL_SECRET: "{{ vault_os_application_credential_secret }}"
+
+
 certbot_fqdn:
   - example.de
 certbot_admin_email: "admin@example.de"
-# certbot_webroot: "/var/www/example"                          # if undefined use --standalone
+# certbot_webroot: "/var/www/example"                                                 # if undefined use --standalone
 
 # application settings
-# certbot_application: "example"                               # if defined copy certs to application dir and setup deploy hooks
+# certbot_application: "example"                                                      # if defined copy certs to application dir and setup deploy hooks
 certbot_application_dir: "/etc/{{ certbot_application }}"
 certbot_application_deploy_hook: |
   #!/bin/sh
diff --git a/tasks/CentOS-packages.yml b/tasks/CentOS-packages-webserver.yml
similarity index 100%
rename from tasks/CentOS-packages.yml
rename to tasks/CentOS-packages-webserver.yml
diff --git a/tasks/CentOS-timer.yml b/tasks/CentOS-timer-webserver.yml
similarity index 100%
rename from tasks/CentOS-timer.yml
rename to tasks/CentOS-timer-webserver.yml
diff --git a/tasks/Debian-packages.yml b/tasks/Debian-packages-webserver.yml
similarity index 100%
rename from tasks/Debian-packages.yml
rename to tasks/Debian-packages-webserver.yml
diff --git a/tasks/Debian-timer.yml b/tasks/Debian-timer-webserver.yml
similarity index 100%
rename from tasks/Debian-timer.yml
rename to tasks/Debian-timer-webserver.yml
diff --git a/tasks/Fedora-packages.yml b/tasks/Fedora-packages-webserver.yml
similarity index 100%
rename from tasks/Fedora-packages.yml
rename to tasks/Fedora-packages-webserver.yml
diff --git a/tasks/Fedora-timer.yml b/tasks/Fedora-timer-webserver.yml
similarity index 100%
rename from tasks/Fedora-timer.yml
rename to tasks/Fedora-timer-webserver.yml
diff --git a/tasks/Ubuntu-packages.yml b/tasks/Ubuntu-packages-webserver.yml
similarity index 100%
rename from tasks/Ubuntu-packages.yml
rename to tasks/Ubuntu-packages-webserver.yml
diff --git a/tasks/Ubuntu-timer.yml b/tasks/Ubuntu-timer-webserver.yml
similarity index 100%
rename from tasks/Ubuntu-timer.yml
rename to tasks/Ubuntu-timer-webserver.yml
diff --git a/tasks/dns-challenge.yml b/tasks/dns-challenge.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ff0cb998e64e7b28d46fa9a8d7c4c4b44d38e496
--- /dev/null
+++ b/tasks/dns-challenge.yml
@@ -0,0 +1,99 @@
+- name: Fetch Binary
+  ansible.builtin.get_url:
+    url: "https://github.com/go-acme/lego/releases/download/v{{ certbot_lego_version }}/lego_v{{ certbot_lego_version }}_linux_amd64.tar.gz"
+    dest: "/tmp/lego_v{{ certbot_lego_version }}_linux_amd64.tar.gz"
+    mode: "0644"
+
+- name: Unpack Archive
+  ansible.builtin.unarchive:
+    src: "/tmp/lego_v{{ certbot_lego_version }}_linux_amd64.tar.gz"
+    dest: "/tmp"
+    remote_src: true
+
+- name: Copy Binary
+  ansible.builtin.copy:
+    src: /tmp/lego
+    dest: /usr/bin/lego
+    owner: root
+    group: root
+    mode: '0755'
+    remote_src: true
+  become: true
+
+- name: Check Whether Cert Exists
+  ansible.builtin.stat:
+    path: "{{ certbot_live_dir }}/fullchain.pem"
+  register: lecert
+  become: true
+
+- name: Ensure Deploy Hook Dir Exists
+  ansible.builtin.file:
+    path: "{{ item }}"
+    state: directory
+    mode: "0755"
+    owner: root
+    group: root
+  loop:
+    - /etc/letsencrypt/renewal-hooks
+    - /etc/letsencrypt/renewal-hooks/deploy
+  become: true
+
+- name: Request Cert If Necessary - DNS Challenge
+  block:
+    - name: Request Cert
+      ansible.builtin.command: >-
+        lego -a --dns {{ certbot_dns_provider }}
+        --email {{ certbot_admin_email }} -d {{ lego_dflag }}
+        --path {{ certbot_live_dir }}
+        run
+      environment: "{{ dns_provider_auth_env_variables }}"
+      register: lego
+      changed_when: lego.rc == 0
+
+    - name: Mirror Letsencrypt Structure
+      ansible.builtin.copy:
+        src: "{{ item.src }}"
+        dest: "{{ item.dest }}"
+        owner: root
+        group: root
+        mode: '0600'
+        remote_src: true
+      loop:
+        - { src: "{{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.crt", dest: "{{ certbot_live_dir }}/fullchain.pem" }
+        - { src: "{{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.key", dest: "{{ certbot_live_dir }}/privkey.pem" }
+
+  when: not lecert.stat.exists
+  become: true
+
+- name: Render Systemd Files
+  block:
+    - name: Render Systemd Environment File
+      ansible.builtin.template:
+        src: templates/dns-challenge.env.j2
+        dest: /etc/default/dns-challenge.env
+        mode: "0644"
+
+    - name: Render Systemd Service File
+      ansible.builtin.template:
+        src: templates/dns-challenge.service.j2
+        dest: /lib/systemd/system/dns-challenge.service
+        mode: "0644"
+
+    - name: Render Systemd Timer File
+      ansible.builtin.template:
+        src: templates/dns-challenge.timer.j2
+        dest: /lib/systemd/system/dns-challenge.timer
+        mode: "0644"
+  
+  become: true
+
+- name: Setup Certbot With Application
+  ansible.builtin.include_tasks: "application.yml"
+  when: certbot_application is defined
+
+- name: Enable LEGO Renew Timer
+  ansible.builtin.systemd:
+    name: dns-challenge.timer
+    state: started
+    enabled: true
+  become: true
diff --git a/tasks/main.yml b/tasks/main.yml
index 3cc640a31d76361ef1fc41cb1c2a1ab1233b7ef6..a97d708121d71398f8e54f12ca9ae0962ee188e1 100644
--- a/tasks/main.yml
+++ b/tasks/main.yml
@@ -1,58 +1,8 @@
 ---
-# TODO:
-#   - dns challenge
+- name: Obtain Cert Using Web Server
+  ansible.builtin.include_tasks: "webserver.yml"
+  when: not certbot_dns_challenge
 
-- name: Install Packages Based On Distribution
-  ansible.builtin.include_tasks: "{{ ansible_facts.distribution }}-packages.yml"
-
-- name: Check Whether Cert Exists
-  ansible.builtin.stat:
-    path: "{{ certbot_live_dir }}/cert.pem"
-  register: lecert
-  become: true
-
-- name: Ensure Deploy Hook Dir Exists
-  ansible.builtin.file:
-    path: "{{ item }}"
-    state: directory
-    mode: "0755"
-    owner: root
-    group: root
-  loop:
-    - /etc/letsencrypt/renewal-hooks
-    - /etc/letsencrypt/renewal-hooks/deploy
-  become: true
-
-- name: Request Cert If Necessary - Standalone
-  ansible.builtin.command: >-
-    certbot certonly --standalone --noninteractive --agree-tos
-    --email {{ certbot_admin_email }} -d {{ certbot_dflag }}
-  when: not lecert.stat.exists and not certbot_webroot is defined
-  register: cbstandalone
-  changed_when: cbstandalone.rc == 0
-  become: true
-
-- name: Ensure Webroot Path Exists
-  ansible.builtin.file:
-    path: "{{ certbot_webroot }}"
-    state: directory
-    mode: "0755"
-    owner: root
-    group: root
-  when: not lecert.stat.exists and certbot_webroot is defined
-
-- name: Request Cert If Necessary - Webroot
-  ansible.builtin.command: >-
-    certbot certonly --webroot --webroot-path {{ certbot_webroot }} --noninteractive --agree-tos
-    --email {{ certbot_admin_email }} -d {{ certbot_dflag }}
-  when: not lecert.stat.exists and certbot_webroot is defined
-  register: cbwebroot
-  changed_when: cbwebroot.rc == 0
-  become: true
-
-- name: Setup Certbot With Application
-  ansible.builtin.include_tasks: "application.yml"
-  when: certbot_application is defined
-
-- name: Enable Letsencrypt Renew Timer Based On Distribution
-  ansible.builtin.include_tasks: "{{ ansible_facts.distribution }}-timer.yml"
+- name: Obtain Cert DNS Challenge
+  ansible.builtin.include_tasks: "dns-challenge.yml"
+  when: certbot_dns_challenge
diff --git a/tasks/webserver.yml b/tasks/webserver.yml
new file mode 100644
index 0000000000000000000000000000000000000000..96e60749ca9688e3a8a7d841435aad74a88a8051
--- /dev/null
+++ b/tasks/webserver.yml
@@ -0,0 +1,54 @@
+- name: Install Packages Based On Distribution
+  ansible.builtin.include_tasks: "{{ ansible_facts.distribution }}-packages-webserver.yml"
+
+- name: Check Whether Cert Exists
+  ansible.builtin.stat:
+    path: "{{ certbot_live_dir }}/cert.pem"
+  register: lecert
+  become: true
+
+- name: Ensure Deploy Hook Dir Exists
+  ansible.builtin.file:
+    path: "{{ item }}"
+    state: directory
+    mode: "0755"
+    owner: root
+    group: root
+  loop:
+    - /etc/letsencrypt/renewal-hooks
+    - /etc/letsencrypt/renewal-hooks/deploy
+  become: true
+
+- name: Request Cert If Necessary - Standalone
+  ansible.builtin.command: >-
+    certbot certonly --standalone --noninteractive --agree-tos
+    --email {{ certbot_admin_email }} -d {{ certbot_dflag }}
+  when: not lecert.stat.exists and not certbot_webroot is defined
+  register: cbstandalone
+  changed_when: cbstandalone.rc == 0
+  become: true
+
+- name: Ensure Webroot Path Exists
+  ansible.builtin.file:
+    path: "{{ certbot_webroot }}"
+    state: directory
+    mode: "0755"
+    owner: root
+    group: root
+  when: not lecert.stat.exists and certbot_webroot is defined
+
+- name: Request Cert If Necessary - Webroot
+  ansible.builtin.command: >-
+    certbot certonly --webroot --webroot-path {{ certbot_webroot }} --noninteractive --agree-tos
+    --email {{ certbot_admin_email }} -d {{ certbot_dflag }}
+  when: not lecert.stat.exists and certbot_webroot is defined
+  register: cbwebroot
+  changed_when: cbwebroot.rc == 0
+  become: true
+
+- name: Setup Certbot With Application
+  ansible.builtin.include_tasks: "application.yml"
+  when: certbot_application is defined
+
+- name: Enable Letsencrypt Renew Timer Based On Distribution
+  ansible.builtin.include_tasks: "{{ ansible_facts.distribution }}-timer-webserver.yml"
diff --git a/templates/dns-challenge.env.j2 b/templates/dns-challenge.env.j2
new file mode 100644
index 0000000000000000000000000000000000000000..efe3629545669c7502a3bcbf42f318cf7361ae7d
--- /dev/null
+++ b/templates/dns-challenge.env.j2
@@ -0,0 +1,5 @@
+# {{ ansible_managed }}
+
+{% for var, val in dns_provider_auth_env_variables.items() %}
+{{ var }}={{ val }}
+{% endfor %}
diff --git a/templates/dns-challenge.service.j2 b/templates/dns-challenge.service.j2
new file mode 100644
index 0000000000000000000000000000000000000000..be5ca9e6101de2cdf7bf238ad53f18417ca26e06
--- /dev/null
+++ b/templates/dns-challenge.service.j2
@@ -0,0 +1,12 @@
+# {{ ansible_managed }}
+
+[Unit]
+Description=LEGO DNS challenge
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/lego -a --dns {{ certbot_dns_provider }} --email {{ certbot_admin_email }} -d {{ lego_dflag }} --path {{ certbot_live_dir }} renew
+ExecStartPost=cp {{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.crt {{ certbot_live_dir }}/fullchain.pem
+ExecStartPost=cp {{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.key {{ certbot_live_dir }}/privkey.pem
+{{ "ExecStartPost=/etc/letsencrypt/renewal-hooks/deploy/" + certbot_application if certbot_application is defined else "" }}
+EnvironmentFile=/etc/default/dns-challenge.env
diff --git a/templates/dns-challenge.timer.j2 b/templates/dns-challenge.timer.j2
new file mode 100644
index 0000000000000000000000000000000000000000..382878a3a4303bd7d8a5dcf0a025202bac1efb66
--- /dev/null
+++ b/templates/dns-challenge.timer.j2
@@ -0,0 +1,13 @@
+# {{ ansible_managed }}
+
+[Unit]
+Description=Run LEGO DNS challenge twice every day
+
+[Timer]
+OnCalendar=*-*-* 00,12:00:00
+RandomizedDelaySec=43200
+Persistent=true
+Unit=dns-challenge.service
+
+[Install]
+WantedBy=timers.target
diff --git a/vars/main.yml b/vars/main.yml
index d12eca560203f5505c97f30a0a9bf820ad5f1208..4a6a30f51c56635de58b4bdf66854a2ae3a05bbb 100644
--- a/vars/main.yml
+++ b/vars/main.yml
@@ -1,9 +1,11 @@
 ---
-certbot_live_dir: >-
-  /etc/letsencrypt/live/{{
+certbot_fqdn_first: >-
+  {{
     certbot_fqdn
     | first
     | replace("*.", "")
     | trim(".")
   }}
+certbot_live_dir: "/etc/letsencrypt/live/{{ certbot_fqdn_first }}"
 certbot_dflag: "{{ certbot_fqdn | map('trim', '.') | join(',') }}"
+lego_dflag : "{{ certbot_fqdn | map('trim', '.') | join(' -d ') }}"