diff --git a/docker/app/.env.dist b/docker/app/.env.dist
new file mode 100644
index 0000000000000000000000000000000000000000..529d56fb807faee2b061bbc58391e6c2c7dd5248
--- /dev/null
+++ b/docker/app/.env.dist
@@ -0,0 +1,3 @@
+POSTGRES_PASSWORD = [CHOOSE A SECURE PASSWORD FOR POSTGRESQL DB]
+BASE_URL = [URL TO HARMONY]
+HTTP = [ HTTP ("http") or HTTPS ("https") ]
\ No newline at end of file
diff --git a/docker/app/.gitignore b/docker/app/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5eaac99fc24ada17d0fb7ad7de46db28e4264ace
--- /dev/null
+++ b/docker/app/.gitignore
@@ -0,0 +1,3 @@
+harmony/**
+pg/**
+.env
\ No newline at end of file
diff --git a/docker/app/docker-compose.yml b/docker/app/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ed01cb82a0961dac01597f12ba09ef736a1fa0cd
--- /dev/null
+++ b/docker/app/docker-compose.yml
@@ -0,0 +1,66 @@
+version: "3.8"
+
+services:
+  pg:
+    image: postgres:16.1-alpine3.19
+    # The container restarts automatically unless you stop it with "docker compose down"
+    restart: unless-stopped
+    networks:
+      - services
+    volumes:
+      # The postgres data is stored in the pg/data folder on your host machine, allowing postgres to persist data even if the container is removed
+      - ./pg/data:/var/lib/postgresql/data
+    environment:
+      POSTGRES_USER: harmony
+      # This is the password you set in the .env file
+      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
+      PGDATA: /var/lib/postgresql/data/pgdata
+
+  web:
+    image: jnslo/harmony:latest
+    # The container restarts automatically unless you stop it with "docker compose down"
+    restart: unless-stopped
+    networks:
+      - appnet
+      - services
+    volumes:
+      # You can override the default configuration by placing a local configuration file in harmony/config/local
+      - ./harmony/config/local:/app/config/local
+    environment:
+      BASE_URL: ${HTTP}://${BASE_URL}
+      DB_HOST: pg
+      DB_PORT: 5432
+      DB_USER: harmony
+      # This is the password you set in the .env file
+      DB_PASS: ${POSTGRES_PASSWORD}
+      DB_NAME: harmony
+    # Attention: You need to either expose the port by uncommenting the following port mapping or by using Traefik (see below).
+    # For production use Traefik is highly recommended as it allows you to use HTTPS and is more secure and isolated than exposing bare ports.
+    ports:
+      # This will bind your host machine's port 8080 to the container's port 8213
+      # In that case the BASE_URL should be localhost:8080 and HTTP should be http.
+      # Disable this for production and use BASE_URL: yourdomain.com and HTTP: https.
+      - "8080:8213"
+    # Uncomment the following labels to allow Traefik to route to this service. See explanations above each label for more information.
+    # Also, see the default /docker/traefik.example setup that is included in this repository. It is not necessary to implement this exact setup,
+    # but it might help you understand how to configure Traefik. For more see the Traefik documentation.
+    #labels:
+      # Enable Traefik for this service, otherwise it will not be routed to
+      #- "traefik.enable=true"
+      # You need to specify the domain name through the .env file (see .env.dist)
+      #- "traefik.http.routers.web.rule=Host(`${BASE_URL}`)"
+      # You need to specify the entrypoint by name that is configured for HTTPS in your Traefik setup, usually "websecure"
+      # HTTPS is required for most oauth providers to work and is recommended for production.
+      #- "traefik.http.routers.web.entrypoints=websecure"
+      # This is important as the default port of HARMONY inside the container is 8213
+      #- "traefik.http.services.web.loadbalancer.server.port=8213"
+      # Optional letsencrypt certificate resolver
+      #- "traefik.http.routers.web.tls.certresolver=letsencrypt"
+
+networks:
+  # This network is used by Traefik to route to services
+  appnet:
+    external: true
+  # This network is used only inside this docker-compose.yml and the associated containers to allow them to communicate with each other
+  services:
+    internal: true
diff --git a/docker/traefik.example/docker-compose.yml b/docker/traefik.example/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4b3a23af54af0f88a19ff1c698bac40da6086f4f
--- /dev/null
+++ b/docker/traefik.example/docker-compose.yml
@@ -0,0 +1,27 @@
+version: "3.8"
+
+services:
+  traefik:
+    image: traefik:v3.0
+    container_name: traefik
+    # Restart the container automatically unless you stop it with "docker compose down"
+    restart: unless-stopped
+    networks:
+      - appnet
+    ports:
+      - "80:80"
+      # Important: HTTPS is required for most oauth providers to work and is recommended for production
+      - "443:443"
+    volumes:
+      # This is necessary for Traefik to be able to route to other containers
+      - /var/run/docker.sock:/var/run/docker.sock
+      # You can override the default configuration by replacing the provided traefik.yml file
+      - ./traefik.yml:/etc/traefik/traefik.yml
+      # This will be handled by Traefik automatically
+      - ./acme.json:/acme.json
+
+networks:
+  # You have to create this network before running "docker compose up" as it is external
+  # This network is used by Traefik to route to services
+  appnet:
+    external: true
diff --git a/docker/traefik.example/traefik.yml.dist b/docker/traefik.example/traefik.yml.dist
new file mode 100644
index 0000000000000000000000000000000000000000..8b1a04cf3ebcef329afe0252d3fec0ae0f3ecbaf
--- /dev/null
+++ b/docker/traefik.example/traefik.yml.dist
@@ -0,0 +1,22 @@
+entryPoints:
+  web:
+    address: ":80"
+  # Important: HTTPS is required for most oauth providers to work and is recommended for production
+  websecure:
+    address: ":443"
+
+providers:
+  docker:
+    # For security reasons, you should not expose all containers by default
+    exposedByDefault: false
+    # This network is used by Traefik to route to services
+    network: appnet
+
+certificatesResolvers:
+  # You can replace this however you want, but if you keep it, you should set the email
+  letsencrypt:
+    acme:
+      email: [! YOUR EMAIL !]
+      storage: acme.json
+      httpChallenge:
+        entryPoint: web