diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..317dee225192ec7dc01c3d21b86167e0e32b5407
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,4 @@
+/src/
+/dist/
+/node_modules/
+/.?*
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..12be6d73aeaa212492d6327ee83ecdcc648f445b
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,26 @@
+FROM node
+
+RUN mkdir -p /src /dist /reveal_js
+RUN ln -snf /src /reveal_js/src
+RUN ln -snf /dist /reveal_js/dist
+
+COPY package*.json /reveal_js/
+
+WORKDIR /reveal_js
+
+RUN npm install
+
+ARG PANDOC_VERSION=2.7.3
+
+ADD https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-linux.tar.gz /tmp/pandoc.tar.gz
+
+RUN tar xvfz /tmp/pandoc.tar.gz -C /usr/local --strip-components=1 \
+    && rm /tmp/pandoc.tar.gz
+
+COPY . /reveal_js
+
+EXPOSE 80/tcp 8899/tcp
+
+ENTRYPOINT ["/reveal_js/node_modules/.bin/grunt"]
+
+CMD ["serve"]
diff --git a/Gruntfile.js b/Gruntfile.js
index 222e27e8c000f8379a046f699232decce87d0369..14988e63615c52d8745c036477aa310de36839e8 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -1,8 +1,12 @@
 const { readdirSync } = require("fs");
 
 module.exports = grunt => {
-  const host = "127.0.0.1";
-  const liveReloadPort = 8899;
+  const PUBLIC_HOST = process.env.PUBLIC_HOST || "localhost";
+  const BIND_IP = process.env.BIND_IP || "0.0.0.0";
+  const BIND_PORT = 0 + process.env.BIND_PORT || 80;
+  const LIVERELOAD_PORT = 0 + process.env.LIVERELOAD_PORT || 8899;
+  const SLIDE_LEVEL = 0 + process.env.SLIDE_LEVEL || 2;
+  const TOC_DEPTH = 0 + process.env.TOC_DEPTH || SLIDE_LEVEL;
 
   const revealJsSources = ["js/**/*.js", "css/**/*.css", "lib/**", "plugin/**"];
 
@@ -11,7 +15,8 @@ module.exports = grunt => {
 
     "http-server": {
       dev: {
-        host,
+        host: BIND_IP,
+        port: BIND_PORT,
         root: "dist",
         runInBackground: true,
         openBrowser: true
@@ -29,7 +34,7 @@ module.exports = grunt => {
           debounceDelay: 1000,
           event: ["add", "change"],
           spawn: false,
-          livereload: liveReloadPort
+          livereload: LIVERELOAD_PORT
         }
       },
       revealjs: {
@@ -52,11 +57,12 @@ module.exports = grunt => {
             `--output=./${pres}${env !== "prod" ? "-" + env : ""}.html`,
             "--standalone",
             "--toc",
-            "--slide-level=2",
+            `--toc-depth=${TOC_DEPTH}`,
+            `--slide-level=${SLIDE_LEVEL}`,
             "--variable=theme:irstea",
             "--variable=history:true",
             env === "dev" &&
-              `--variable=header-includes='<script src="//${host}:${liveReloadPort}/livereload.js"></script>'`
+              `--variable=header-includes='<script src="//${PUBLIC_HOST}:${LIVERELOAD_PORT}/livereload.js"></script>'`
           ]
             .filter(v => !!v)
             .join(" "),