diff --git a/.env.example b/.env.example
index ab4597a4..917a8d65 100644
--- a/.env.example
+++ b/.env.example
@@ -1 +1,36 @@
-KEY=32_character_encryption_key
+ENCRYPTION_KEY=32_character_encryption_key
+
+CLIENT_ID=
+CLIENT_SECRET=
+BOT_TOKEN=
+
+OAUTH_CALLBACK_URL=
+DASHBOARD_DOMAIN=
+API_DOMAIN=
+PORT=443
+
+# The MySQL database running in the container is exposed to the host on this port,
+# allowing access with database tools such as DBeaver
+MYSQL_PORT=3001
+# Password for the Zeppelin database user
+MYSQL_PASSWORD=
+# Password for the MySQL root user
+MYSQL_ROOT_PASSWORD=
+
+# The development environment container has an SSH server that you can connect to.
+# This is the port that server is exposed to the host on.
+DEVELOPMENT_SSH_PORT=3002
+
+# Only required if relevant feature is used
+#PHISHERMAN_API_KEY=
+
+# In production, the newest code is pulled from a repository
+# Specify that repository URL here
+PRODUCTION_REPOSITORY=https://github.com/ZeppelinBot/Zeppelin.git
+
+# You only need to set these if you're running an external database.
+# In a standard setup, the database is run in a docker container.
+#DB_HOST=
+#DB_USER=
+#DB_PASSWORD=
+#DB_DATABASE=
diff --git a/backend/api.env.example b/backend/api.env.example
deleted file mode 100644
index 87afe866..00000000
--- a/backend/api.env.example
+++ /dev/null
@@ -1,10 +0,0 @@
-PORT=
-CLIENT_ID=
-CLIENT_SECRET=
-OAUTH_CALLBACK_URL=
-DASHBOARD_URL=
-DB_HOST=
-DB_USER=
-DB_PASSWORD=
-DB_DATABASE=
-STAFF=
diff --git a/backend/bot.env.example b/backend/bot.env.example
deleted file mode 100644
index b701f4ed..00000000
--- a/backend/bot.env.example
+++ /dev/null
@@ -1,7 +0,0 @@
-TOKEN=
-DB_HOST=
-DB_USER=
-DB_PASSWORD=
-DB_DATABASE=
-PROFILING=false
-PHISHERMAN_API_KEY=
diff --git a/docker-compose-dev.sh b/docker-compose-dev.sh
new file mode 100755
index 00000000..14af4e94
--- /dev/null
+++ b/docker-compose-dev.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+DOCKER_UID="$(id -u)" DOCKER_GID="$(id -g)" docker-compose --env-file ./.env -f ./docker/development/docker-compose.yml "$@"
diff --git a/docker/development/devenv/Dockerfile b/docker/development/devenv/Dockerfile
new file mode 100644
index 00000000..95e3e128
--- /dev/null
+++ b/docker/development/devenv/Dockerfile
@@ -0,0 +1,18 @@
+FROM ubuntu:20.04
+
+ARG DOCKER_UID
+
+ENV DEBIAN_FRONTEND=noninteractive
+ENV TZ=UTC
+
+# Set up SSH access
+RUN apt-get update && apt-get install -y openssh-server sudo git
+RUN mkdir /var/run/sshd
+RUN useradd -rm -d /home/ubuntu -s /bin/bash -g root -G sudo -u "${DOCKER_UID}" ubuntu
+RUN echo 'ubuntu:password' | chpasswd
+
+# Install Node.js 16
+RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash -
+RUN apt-get install -y nodejs
+
+CMD /usr/sbin/sshd -D -e
diff --git a/docker/development/docker-compose.yml b/docker/development/docker-compose.yml
new file mode 100644
index 00000000..c5721914
--- /dev/null
+++ b/docker/development/docker-compose.yml
@@ -0,0 +1,56 @@
+version: '3'
+services:
+#  nginx:
+#    user: "${UID:?Missing UID}:${GID:?Missing GID}"
+#    build:
+#      context: ./nginx
+#      args:
+#        API_DOMAIN: ${API_DOMAIN:?Missing API_DOMAIN}
+#        API_PORT: ${API_PORT:?Missing API_PORT}
+#        DASHBOARD_DOMAIN: ${DASHBOARD_DOMAIN:?Missing DASHBOARD_DOMAIN}
+#        DASHBOARD_PORT: ${DASHBOARD_PORT:?Missing DASHBOARD_PORT}
+#    ports:
+#      - ${PORT:?Missing PORT}:443
+#    volumes:
+#      - ./:/zeppelin
+#
+  mysql:
+    image: mysql:8.0
+    environment:
+      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD?:Missing MYSQL_ROOT_PASSWORD}
+      MYSQL_DATABASE: zeppelin
+      MYSQL_USER: zeppelin
+      MYSQL_PASSWORD: ${MYSQL_PASSWORD?:Missing MYSQL_PASSWORD}
+    ports:
+      - ${MYSQL_PORT:?Missing MYSQL_PORT}:3306
+#
+#  backend:
+#    image: node:16
+#    user: "${UID:?Missing UID}:${GID:?Missing GID}"
+#    working_dir: /zeppelin/backend
+#    restart: always
+#    depends_on:
+#      - mysql
+#    volumes:
+#      - ./:/zeppelin
+#    command: sh -c "npm run migrate-dev && npm run watch"
+#
+#  dashboard:
+#    image: node:16
+#    user: "${UID:?Missing UID}:${GID:?Missing GID}"
+#    working_dir: /zeppelin/dashboard
+#    restart: always
+#    volumes:
+#      - ./:/zeppelin
+#    command: sh -c "npm run watch-build"
+
+  devenv:
+    build:
+      context: ./devenv
+      args:
+        DOCKER_UID: ${DOCKER_UID:?Missing DOCKER_UID}
+        DOCKER_GID: ${DOCKER_GID:?Missing DOCKER_GID}
+    ports:
+      - "${DEVELOPMENT_SSH_PORT:?Missing DEVELOPMENT_SSH_PORT}:22"
+    volumes:
+      - ../../:/zeppelin
diff --git a/docker/development/nginx/Dockerfile b/docker/development/nginx/Dockerfile
new file mode 100644
index 00000000..7058f8b0
--- /dev/null
+++ b/docker/development/nginx/Dockerfile
@@ -0,0 +1,12 @@
+FROM nginx
+
+ARG API_DOMAIN
+ARG DASHBOARD_DOMAIN
+
+RUN apt-get update && apt-get install -y openssl
+RUN openssl req -x509 -newkey rsa:4096 -keyout /etc/ssl/private/api-cert.key -out /etc/ssl/certs/api-cert.pem -days 365 -subj '/CN=*.${API_DOMAIN}' -nodes
+RUN openssl req -x509 -newkey rsa:4096 -keyout /etc/ssl/private/dashboard-cert.key -out /etc/ssl/certs/dashboard-cert.pem -days 365 -subj '/CN=*.${DASHBOARD_DOMAIN}' -nodes
+
+COPY ./default.conf /etc/nginx/conf.d/default.conf
+RUN sed -ir "s/_API_DOMAIN_/$(echo ${API_DOMAIN} | sed -ir 's///g')/g"
+RUN sed -ir "s/_DASHBOARD_DOMAIN_/$(echo ${DASHBOARD_DOMAIN} | sed 's/\./\\\\./g')/g"
diff --git a/docker/development/nginx/default.conf b/docker/development/nginx/default.conf
new file mode 100644
index 00000000..6aea2aeb
--- /dev/null
+++ b/docker/development/nginx/default.conf
@@ -0,0 +1,44 @@
+server {
+  listen 443 ssl http2;
+  listen [::]:443 ssl http2;
+  server_name _API_DOMAIN_;
+
+  location / {
+    proxy_pass backend:3000;
+
+    client_max_body_size 200M;
+  }
+
+  ssl_certificate /etc/ssl/certs/api-cert.pem;
+  ssl_certificate_key /etc/ssl/private/api-cert.key;
+
+  ssl_session_timeout 1d;
+  ssl_session_cache shared:MozSSL:10m;
+  ssl_session_tickets off;
+
+  ssl_protocols TLSv1.3;
+  ssl_prefer_server_ciphers off;
+}
+
+server {
+  listen 443 ssl http2;
+  listen [::]:443 ssl http2;
+  server_name dashboard.dev.zeppelin.gg;
+
+  root /zeppelin/dashboard/dist;
+
+  location / {
+    index index.html;
+    try_files $uri $uri/ /index.html;
+  }
+
+  ssl_certificate /etc/ssl/certs/dashboard-cert.pem;
+  ssl_certificate_key /etc/ssl/private/dashboard-cert.key;
+
+  ssl_session_timeout 1d;
+  ssl_session_cache shared:MozSSL:10m;
+  ssl_session_tickets off;
+
+  ssl_protocols TLSv1.3;
+  ssl_prefer_server_ciphers off;
+}