diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..d882cf2cff897893aca36c9da3485245be2c0220
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "plugins/pico-cache"]
+	path = plugins/pico-cache
+    url = https://github.com/Tetras-Libre/pico_cache.git
diff --git a/Readme.md b/Readme.md
index 0af94e5c3c6d6722532219254907983778c6c25c..b98613eef02b2aa7e322936d9cd8fe4de04cc57e 100644
--- a/Readme.md
+++ b/Readme.md
@@ -11,6 +11,15 @@
 1. Put your contents in `content` directory
 2. Put images etc in `assets` directory
 
+### Cache
+
+By default an HTML cache is enable with a lifetime of 7 days. This means that **your changes will not be visible until you clear the cache**
+
+To clear the cache, simply run `./pico cache`.
+
+You can change this behavior in the config file.
+
+
 ### Customization
 
 #### Plugins / themes
diff --git a/config/config.yml b/config/config.yml
index ebc92de7620768444a2cad4eed94f5699f032d0b..897e68796239fe86184d2d5e1026a22eec432e4a 100644
--- a/config/config.yml
+++ b/config/config.yml
@@ -22,7 +22,7 @@ twig_config:                        # Twig template engine config
     strict_variables: false         # If set to true, Twig will bail out when unset variables are being used
     charset: utf-8                  # The charset used by Twig templates
     debug: ~                        # Enable Twig's debug mode
-    cache: false                    # Enable Twig template caching by specifying a path to a writable directory
+    cache: content/cache/twig                    # Enable Twig template caching by specifying a path to a writable directory
     auto_reload: ~                  # Recompile Twig templates whenever the source code changes
 
 ##
@@ -58,3 +58,13 @@ DummyPlugin.enabled: false          # Force the plugin "DummyPlugin" to be disab
 # Custom
 #
 my_custom_setting: Hello World!     # You can access custom settings in themes using {{ config.my_custom_setting }}
+
+##
+# Cache
+##
+PicoZCache:
+  enabled: true          # True/False if cache is enabled
+  dir: content/cache/html/    # Directory where cache should be saved
+  time: 2592000           # Interval between caching (period from one to second cache) in seconds, here is 7 days = 60 * 60 * 24 * 30.
+  xhtml_output: false    # If true, XHTML Content-Type header will be sent when loading cache page
+  ignore_url_regex: "/blog/"
diff --git a/config/config.yml.template b/config/config.yml.template
index 181c17d9e816293ea1bb807e670d8f3c23c542de..837df63e63d412a1306741ce1199b991a3dee4aa 100644
--- a/config/config.yml.template
+++ b/config/config.yml.template
@@ -22,7 +22,7 @@ twig_config:                        # Twig template engine config
     strict_variables: false         # If set to true, Twig will bail out when unset variables are being used
     charset: utf-8                  # The charset used by Twig templates
     debug: ~                        # Enable Twig's debug mode
-    cache: false                    # Enable Twig template caching by specifying a path to a writable directory
+    cache: content/cache/twig                    # Enable Twig template caching by specifying a path to a writable directory
     auto_reload: ~                  # Recompile Twig templates whenever the source code changes
 
 ##
@@ -58,3 +58,13 @@ DummyPlugin.enabled: false          # Force the plugin "DummyPlugin" to be disab
 # Custom
 #
 my_custom_setting: Hello World!     # You can access custom settings in themes using {{ config.my_custom_setting }}
+
+##
+# Cache
+##
+PicoZCache:
+  enabled: true          # True/False if cache is enabled
+  dir: content/cache/html/    # Directory where cache should be saved
+  time: 2592000           # Interval between caching (period from one to second cache) in seconds, here is 7 days = 60 * 60 * 24 * 30.
+  xhtml_output: false    # If true, XHTML Content-Type header will be sent when loading cache page
+  ignore_url_regex: "/blog/"
diff --git a/content/cache/.gitignore b/content/cache/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..72e8ffc0db8aad71a934dd11e5968bd5109e54b4
--- /dev/null
+++ b/content/cache/.gitignore
@@ -0,0 +1 @@
+*
diff --git a/docker-compose.yml b/docker-compose.yml
index 2e32794d220b0410f4d1f4aa88769cb6d8d2f221..9149885c6142feca65bd20ff1b2448a39c968682 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -7,3 +7,4 @@ services:
       - "./assets:/var/www/html/assets"
       - "./content:/var/www/html/content"
       - "./config:/var/www/html/config"
+      - "./plugins/pico-cache/PicoZCache.php:/var/www/html/plugins/PicoZCache.php"
diff --git a/pico b/pico
new file mode 100755
index 0000000000000000000000000000000000000000..b8b7b4b1551c73ceeb776f70b6cad7deaced7ad4
--- /dev/null
+++ b/pico
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+usage() {
+    echo "Usage $0 [command]"
+    echo "Commands"
+    echo -e "\tcache clear the HTML cache"
+    echo -e "\tperms fix permissions"
+    echo -e "\thelp display this message and exit"
+}
+docker="docker-compose exec pico"
+
+case $1 in
+    "cache")
+        $docker rm -rf content/cache/html/*
+        $docker rm -rf content/cache/twig/*
+        ;;
+    "perms")
+        $docker chown -R $(id -u):33 content/
+        $docker chmod -R g+w content/cache
+        ;;
+    "help")
+        usage
+        ;;
+    *)
+        echo "Unknown command '$1'"
+        usage
+        exit 1
+esac
diff --git a/plugins/pico-cache b/plugins/pico-cache
new file mode 160000
index 0000000000000000000000000000000000000000..e22f9ece98272cd63b6ffa753c45483be85f380b
--- /dev/null
+++ b/plugins/pico-cache
@@ -0,0 +1 @@
+Subproject commit e22f9ece98272cd63b6ffa753c45483be85f380b