diff --git a/files/geant_acme.py b/files/geant_acme.py
index 9e8d9285f451a40fd418772a9b9b0b47b33ee006..9a9959828d6409cb9010d0d1eb5eb2473ee10d8c 100755
--- a/files/geant_acme.py
+++ b/files/geant_acme.py
@@ -14,6 +14,7 @@ Options:
 """
 import os
 import time
+import datetime
 import subprocess as sp
 import configparser
 from docopt import docopt
@@ -22,14 +23,22 @@ import requests
 BASE_URL = 'https://infoblox.geant.org/wapi/v2.6.1'
 
 
-def os_exit(verbose=None):
+def os_exit():
     """ exit """
-    if verbose:
-        print('+' + 72*'-' + '+')
+    print('+' + 72*'-' + '+')
+    # close logging
+    end_date = time_log()
+    print('[{}] Job ended'.format(end_date))
+    print('+' + 72*'=' + '+')
     os.sys.exit()
 
 
-def get_reference(iblox_domain, iblox_user, iblox_pw, verbose=None):
+def time_log():
+    """ return day and time """
+    return datetime.datetime.now().strftime("%d %b %Y %H:%M:%S")
+
+
+def get_reference(iblox_domain, iblox_user, iblox_pw):
     """ grab reference for txt object """
     ref_obj = requests.get(
         '{}/record:txt?name=_acme-challenge.{}&_return_as_object=1'.format(
@@ -45,35 +54,32 @@ def get_reference(iblox_domain, iblox_user, iblox_pw, verbose=None):
     except IndexError:
         ref = None
 
-    if verbose:
-        if ref:
-            print('got reference object: {}'.format(ref))
-        else:
-            print('{} empty reference object: no challenge to delete'.format(
-                iblox_domain))
-        print('+' + 72*'-' + '+')
+    if ref:
+        print('got reference object: {}'.format(ref))
+    else:
+        print('{} empty reference object: no challenge to delete'.format(
+            iblox_domain))
+    print('+' + 72*'-' + '+')
 
     return ref
 
 
-def delete_challenge(object_reference, iblox_user, iblox_pw, verbose=None):
+def delete_challenge(object_reference, iblox_user, iblox_pw):
     """ delete txt record """
     del_req = requests.delete(
         '{}/{}'.format(BASE_URL, object_reference),
         auth=(iblox_user, iblox_pw)
     )
-    if verbose:
-        print('delete challenge - http code (200 expected): {}'.format(del_req.status_code))
-        print('+' + 72*'-' + '+')
+    print('delete challenge - http code (200 expected): {}'.format(del_req.status_code))
+    print('+' + 72*'-' + '+')
 
     return del_req.status_code
 
 
-def create_challenge(iblox_domain, acme_token, iblox_user, iblox_pw, verbose=None):
+def create_challenge(iblox_domain, acme_token, iblox_user, iblox_pw):
     """ upload txt record """
-    if verbose:
-        print('+' + 72*'-' + '+')
-        print('creating challenge _acme-challenge.{}'.format(iblox_domain))
+    print('+' + 72*'-' + '+')
+    print('creating challenge _acme-challenge.{}'.format(iblox_domain))
 
     post_req = requests.post(
         '{}/record:txt'.format(BASE_URL),
@@ -85,15 +91,14 @@ def create_challenge(iblox_domain, acme_token, iblox_user, iblox_pw, verbose=Non
             "view": "External"
         }
     )
-    if verbose:
-        print('create challenge - http code (201 expected): {} {}'.format(
-            post_req.status_code, post_req.reason))
-        print('+' + 72*'-' + '+')
+    print('create challenge - http code (201 expected): {} {}'.format(
+        post_req.status_code, post_req.reason))
+    print('+' + 72*'-' + '+')
 
     return post_req.status_code
 
 
-def run_certbot(cbot_domain, wild_card=None, verbose=None):
+def run_certbot(cbot_domain, wild_card=None):
     """ get certificate from letsencrypt """
     if wild_card:
         domain_list = '*.{}'.format(cbot_domain)
@@ -104,9 +109,7 @@ def run_certbot(cbot_domain, wild_card=None, verbose=None):
                + ' --manual-auth-hook=/root/bin/infoblox_hook.py' \
                + ' --cert-name {} -d {}'.format(cbot_domain[0], domain_list)
 
-    if verbose:
-        print('+' + 72*'-' + '+')
-        print('running: {}'.format(cbot_cmd))
+    print('running: {}'.format(cbot_cmd))
 
     cbot_child = sp.Popen(cbot_cmd, stdout=sp.PIPE, stderr=sp.PIPE, shell=True)
     cbot_out, cbot_err = cbot_child.communicate()
@@ -116,8 +119,7 @@ def run_certbot(cbot_domain, wild_card=None, verbose=None):
         decoded_msg = cbot_out.decode("utf-8")
         msg = decoded_msg[:decoded_msg.rfind('\n')]
 
-    if verbose:
-        print('\n{}'.format(msg))
+    print('\n{}'.format(msg))
 
     return msg
 
@@ -130,6 +132,14 @@ if __name__ == "__main__":
     CLIENTS = ARGS['--client']
     WILDCARD = ARGS['--wildcard']
     VERBOSE = ARGS['--verbose']
+    if not VERBOSE:
+        os.sys.stdout = open('/var/log/acme/acme.log', 'a')
+
+    # start logging
+    START_DATE = time_log()
+    CMD_LINE = ' '.join(os.sys.argv)
+    print('+' + 72*'=' + '+')
+    print('[{}] Jost started: {}'.format(START_DATE, CMD_LINE))
 
     CONFIG = configparser.RawConfigParser()
     CONFIG.read_file(open('/root/.geant_acme.ini'))
@@ -141,47 +151,44 @@ if __name__ == "__main__":
     else:
         REAL_DOMAIN = 'host {}'.format(DOMAIN[0])
 
-    if VERBOSE:
-        print('+' + 72*'-' + '+')
+    print('+' + 72*'-' + '+')
 
     # Maybe there is an Acme challenge left over. We try to delete it first.
     for domain_item in DOMAIN:
-        REF_OBJ = get_reference(domain_item, IBLOX_USER, IBLOX_PASS, VERBOSE)
+        REF_OBJ = get_reference(domain_item, IBLOX_USER, IBLOX_PASS)
         if REF_OBJ == 'error':
             print('error retrieving reference object')
-            os_exit(VERBOSE)
+            os_exit()
         if REF_OBJ:
             DEL_STATUS = delete_challenge(
-                REF_OBJ, IBLOX_USER, IBLOX_PASS, VERBOSE)
+                REF_OBJ, IBLOX_USER, IBLOX_PASS)
             if DEL_STATUS != 200:
                 print('{}: {} error deleting challenge on Infoblox'.format(
                     domain_item, DEL_STATUS))
-                os_exit(VERBOSE)
+                os_exit()
 
     # run certbot
-    run_certbot(DOMAIN, WILDCARD, VERBOSE)
-    if VERBOSE:
-        print('sleep 10 seconds to wait for DNS to settle down')
-        print('+' + 72*'-' + '+')
+    run_certbot(DOMAIN, WILDCARD)
+    print('sleep 10 seconds to wait for DNS to settle down')
+    print('+' + 72*'-' + '+')
     time.sleep(10)
 
     # remove the Acme challenges from Infoblox
     for domain_item in DOMAIN:
-        REF_OBJ = get_reference(domain_item, IBLOX_USER, IBLOX_PASS, VERBOSE)
+        REF_OBJ = get_reference(domain_item, IBLOX_USER, IBLOX_PASS)
         if REF_OBJ == 'error':
             print('error retrieving reference object')
-            os_exit(VERBOSE)
+            os_exit()
         if REF_OBJ:
             DEL_STATUS = delete_challenge(
-                REF_OBJ, IBLOX_USER, IBLOX_PASS, VERBOSE)
+                REF_OBJ, IBLOX_USER, IBLOX_PASS)
             if DEL_STATUS != 200:
                 print('{}: {} error deleting challenge on Infoblox'.format(
                     domain_item, DEL_STATUS))
-                os_exit(VERBOSE)
-
+                os_exit()
 
-    os.sys.exit()
     # if we are here, everything went fine and we can upload the certificates
+    os.sys.stdout.flush()
     if WILDCARD:
         UPLOADER = '/root/bin/upload_wildcards.py'
         if VERBOSE:
@@ -195,4 +202,4 @@ if __name__ == "__main__":
                 UPLOADER += ' -v'
             os.system(UPLOADER)
 
-    os_exit(VERBOSE)
+    os_exit()
diff --git a/files/geant_acme_uploader.py b/files/geant_acme_uploader.py
index a4dc5787cbc477a030eb581382625278ba978654..0674328e915652cd3dcdb1d9d3f9ac509fa3cae7 100755
--- a/files/geant_acme_uploader.py
+++ b/files/geant_acme_uploader.py
@@ -76,6 +76,8 @@ if __name__ == "__main__":
     DOMAIN = ARGS['--domain']
     CLIENT = ARGS['--client']
     VERBOSE = ARGS['--verbose']
+    if not VERBOSE:
+        os.sys.stdout = open('/var/log/geant_acme.log', 'a')
 
     BASEDIR = '/etc/letsencrypt/live'
 
@@ -89,8 +91,7 @@ if __name__ == "__main__":
             redis_full_path = '{}:redis_{}_{}'.format(
                 CLIENT, domain_underscored, certname_renamed)
 
-            if VERBOSE:
-                print('uploading to Redis: {}'.format(redis_full_path))
+            print('uploading to Redis: {}'.format(redis_full_path))
             redis_upload(REDIS_HOST, REDIS_TOKEN, redis_full_path, certdata)
 
     with open(os.path.join(BASEDIR, DOMAIN, 'privkey.pem'), 'r') as keyfile:
@@ -99,8 +100,8 @@ if __name__ == "__main__":
         VAULT_FULL_PATH = 'puppet/{}/vault_wildcard_{}.key'.format(
             CLIENT, DOMAIN_UNDERSCORED)
 
-        if VERBOSE:
-            print('uploading to Vault: {}'.format(VAULT_FULL_PATH))
+        print('uploading to Vault: {}'.format(VAULT_FULL_PATH))
         vault_upload(VAULT_HOST, VAULT_TOKEN, VAULT_FULL_PATH, KEYDATA)
 
     redis_save(REDIS_HOST, REDIS_TOKEN)
+    os.sys.stdout.flush()
diff --git a/files/logrotate_acme b/files/logrotate_acme
new file mode 100644
index 0000000000000000000000000000000000000000..85c55ea7453d19866c3d8ec6e6e353fb2c39735d
--- /dev/null
+++ b/files/logrotate_acme
@@ -0,0 +1,11 @@
+# ACME log rotation
+#
+/var/log/acme/acme.log {
+    create 660 root root
+    notifempty
+    daily
+    rotate 10
+    missingok
+    compress
+    endscript
+}
diff --git a/files/upload_wildcards.py b/files/upload_wildcards.py
index 0897e709a0d610bf19f9b940f0b82e788f38cf2f..1a8e39b508e7173c5fb6de4f8ede10b5176c9f48 100755
--- a/files/upload_wildcards.py
+++ b/files/upload_wildcards.py
@@ -83,8 +83,7 @@ if __name__ == "__main__":
                 keyname_underscored = keyname.replace('.', '_')
                 redis_full_path = 'common:redis_{}_{}'.format(
                     domain_underscored, keyname_underscored)
-                if VERBOSE:
-                    print('uploading to Redis: {}'.format(redis_full_path))
+                print('uploading to Redis: {}'.format(redis_full_path))
                 redis_upload(REDIS_HOST, REDIS_TOKEN, redis_full_path, keydata)
         with open(os.path.join(BASEDIR, domain, 'privkey.pem'), 'r') as keyfile:
             keydata = keyfile.read()
@@ -92,8 +91,7 @@ if __name__ == "__main__":
             keyname_underscored = 'privkey.pem'.replace('.', '_')
             vault_full_path = 'puppet/common/vault_wildcard_{}_{}'.format(
                 domain_underscored, keyname_underscored)
-            if VERBOSE:
-                print('uploading to Vault: {}'.format(vault_full_path))
+            print('uploading to Vault: {}'.format(vault_full_path))
             vault_upload(VAULT_HOST, VAULT_TOKEN, vault_full_path, keydata)
 
     redis_save(REDIS_HOST, REDIS_TOKEN)
diff --git a/manifests/files.pp b/manifests/files.pp
index ec63754f3ae0da2ec3ccb080244646103a2c2921..1cfdf6d0d5f3af88563f4400279cf81ca5f9a322 100644
--- a/manifests/files.pp
+++ b/manifests/files.pp
@@ -34,6 +34,8 @@ class geant_acme::files (
       source => "puppet:///modules/${module_name}/geant_acme_uploader.py";
     '/root/bin/upload_wildcards.py':
       source => "puppet:///modules/${module_name}/upload_wildcards.py";
+    '/root/bin/infoblox_hook.py':
+      source => "puppet:///modules/${module_name}/infoblox_hook.py";
     '/etc/letsencrypt/cli.ini':
       mode    => '0644',
       require => File['/etc/letsencrypt'],
@@ -41,6 +43,11 @@ class geant_acme::files (
     '/root/.geant_acme.ini':
       mode    => '0640',
       content => Sensitive(template("${module_name}/geant_acme.ini.erb"));
+    '/var/log/acme':
+      ensure => directory;
+    '/etc/logrotate.d/acme':
+      mode   => '0644',
+      source => "puppet:///modules/${module_name}/logrotate_acme";
   }
 
 }