diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..909c576
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,5 @@
+DATABASE=OpenPlzApi
+DBUSERNAME=plzuser
+DBPASSWORD=qwertz
+ENVIRONMENT=Development
+TZ=Europe/Berlin
diff --git a/.gitignore b/.gitignore
index 3260fc8..9c730df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -363,3 +363,9 @@ FodyWeavers.xsd
# No development configurations
appsettings.Development.json
+
+# No Docker environment configurations
+.env
+
+# No downloads
+downloads/
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..73a3400
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,56 @@
+services:
+ db:
+ image: postgres:18-alpine
+ container_name: openplz-db
+ restart: unless-stopped
+ shm_size: 256mb
+ volumes:
+ - pgdata:/var/lib/postgresql
+ environment:
+ POSTGRES_DB: ${DATABASE}
+ POSTGRES_USER: ${DBUSERNAME}
+ POSTGRES_PASSWORD: ${DBPASSWORD}
+
+ api:
+ image: openplzapi-webservice:latest
+ build:
+ context: ./src
+ dockerfile: ./webservice/dockerfile
+ container_name: openplz-api
+ restart: unless-stopped
+ depends_on:
+ - db
+ environment:
+ ASPNETCORE_ENVIRONMENT: ${ENVIRONMENT}
+ ASPNETCORE_HTTP_PORTS: 8080
+ Database__Server: db
+ Database__Database: ${DATABASE}
+ Database__Username: ${DBUSERNAME}
+ Database__Password: ${DBPASSWORD}
+ TZ: ${TZ:-Europe/Berlin}
+ ports:
+ - 8080:8080
+
+ cli:
+ image: openplzapi-cli:latest
+ build:
+ context: ./src
+ dockerfile: ./cli/dockerfile
+ container_name: openplz-cli
+ # uncomment the following line for initialization of the database, then comment it again for regular usage
+ # command: ["initdb","--import"]
+ restart: "no"
+ depends_on:
+ - db
+ environment:
+ ASPNETCORE_ENVIRONMENT: ${ENVIRONMENT}
+ Database__Server: db
+ Database__Database: ${DATABASE}
+ Database__Username: ${DBUSERNAME}
+ Database__Password: ${DBPASSWORD}
+ TZ: ${TZ:-Europe/Berlin}
+ volumes:
+ - ./downloads:/downloads
+
+volumes:
+ pgdata:
diff --git a/src/cli/OpenPlzApi.CLI.csproj b/src/cli/OpenPlzApi.CLI.csproj
index ff6576d..74da1d1 100644
--- a/src/cli/OpenPlzApi.CLI.csproj
+++ b/src/cli/OpenPlzApi.CLI.csproj
@@ -27,6 +27,7 @@
+
@@ -35,7 +36,7 @@
-
+
diff --git a/src/cli/Program.cs b/src/cli/Program.cs
index 0a77970..12886a1 100644
--- a/src/cli/Program.cs
+++ b/src/cli/Program.cs
@@ -38,6 +38,7 @@ public static async Task Main(string[] args)
.AddJsonFile("appsettings.json", optional: false)
.AddJsonFile("appsettings.Development.json", optional: true)
.AddJsonFile("appsettings.Production.json", optional: true)
+ .AddEnvironmentVariables()
.Build();
// Bind configuration
diff --git a/src/cli/dockerfile b/src/cli/dockerfile
new file mode 100644
index 0000000..e8224ae
--- /dev/null
+++ b/src/cli/dockerfile
@@ -0,0 +1,20 @@
+FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
+WORKDIR /App
+
+# Copy everything
+COPY ./cli ./
+COPY ./datalayer /datalayer
+# Restore as distinct layers
+RUN dotnet restore
+# Build and publish a release
+RUN dotnet publish -o out
+
+# Build runtime image
+FROM mcr.microsoft.com/dotnet/runtime:10.0
+WORKDIR /App
+COPY --from=build /App/out .
+ENV Sources__RootFolderName=/downloads
+RUN mkdir /downloads
+ENTRYPOINT ["dotnet", "OpenPlzApi.CLI.dll"]
+# Default to showing help when no command is provided; user can pass any subcommand
+CMD ["-h"]
diff --git a/src/webservice/dockerfile b/src/webservice/dockerfile
new file mode 100644
index 0000000..1deb93d
--- /dev/null
+++ b/src/webservice/dockerfile
@@ -0,0 +1,18 @@
+FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
+WORKDIR /App
+
+# Copy everything
+COPY ./webservice ./
+COPY ./datalayer /datalayer
+# Restore as distinct layers
+RUN dotnet restore
+# Build and publish a release
+RUN dotnet publish -o out
+
+# Build runtime image
+FROM mcr.microsoft.com/dotnet/aspnet:10.0
+WORKDIR /App
+COPY --from=build /App/out .
+EXPOSE 8080
+USER $APP_UID
+ENTRYPOINT ["dotnet", "OpenPlzApi.WebService.dll"]