diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000..ad78f13
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,145 @@
+#
+# AWG GOLANG BUILDING STAGE
+# Base: Alpine
+#
+
+# Pull the current golang-alpine image.
+FROM golang:1.25-alpine3.23 AS awg-go
+
+# Install build-dependencies.
+RUN apk add --no-cache \
+ git \
+ gcc \
+ musl-dev
+
+# Standard working directory for WGDashboard
+RUN mkdir -p /workspace && \
+ git clone https://github.com/WGDashboard/amneziawg-go /workspace/awg
+
+# Enable CGO compilation for AmneziaWG
+ENV CGO_ENABLED=1
+
+# Change directory
+WORKDIR /workspace/awg
+# Compile the binaries
+RUN go version && \
+ go mod download && \
+ go mod verify && \
+ go build -ldflags '-linkmode external -extldflags "-fno-PIC -static"' -v -o /usr/bin
+#
+# AWG TOOLS BUILDING STAGE
+# Base: Alpine
+#
+FROM alpine:3.23 AS awg-tools
+
+# Install needed dependencies.
+RUN apk add --no-cache \
+ make \
+ git \
+ build-base \
+ linux-headers \
+ ca-certificates
+
+# Get the workspace ready
+RUN mkdir -p /workspace && \
+ git clone https://github.com/WGDashboard/amneziawg-tools /workspace/awg-tools
+
+# Change directory
+WORKDIR /workspace/awg-tools/src
+# Compile and change permissions
+RUN make && chmod +x wg*
+
+#
+# PIP DEPENDENCY BUILDING
+# Base: Alpine
+#
+
+# Use the python-alpine image for building pip dependencies
+FROM python:3.14-alpine3.23 AS pip-builder
+
+ARG TARGETPLATFORM
+
+# Add the build dependencies and create a Python virtual environment.
+RUN apk add --no-cache \
+ build-base \
+ pkgconfig \
+ python3-dev \
+ postgresql-dev \
+ libffi-dev \
+ libpq \
+ linux-headers \
+ rust \
+ cargo \
+ && mkdir -p /opt/wgdashboard/src \
+ && python3 -m venv /opt/wgdashboard/src/venv
+
+# Copy the requirements file into the build layer.
+COPY ./src/requirements.txt /opt/wgdashboard/src
+RUN if [ "$TARGETPLATFORM" = "linux/arm/v7" ]; then \
+ sed -i 's|psycopg\[binary\]|psycopg[c]|' /opt/wgdashboard/src/requirements.txt; \
+ fi; \
+ cat /opt/wgdashboard/src/requirements.txt
+
+# Install the pip packages
+RUN . /opt/wgdashboard/src/venv/bin/activate && \
+ pip3 install --upgrade pip && \
+ pip3 install -r /opt/wgdashboard/src/requirements.txt
+
+#
+# WGDashboard RUNNING STAGE
+# Base: Alpine
+#
+
+# Running with the python-alpine image.
+FROM python:3.14-alpine3.23 AS final
+LABEL maintainer="dselen@nerthus.nl"
+
+# Install only the runtime dependencies
+RUN apk add --no-cache \
+ iproute2 iptables \
+ bash curl procps openrc \
+ tzdata wireguard-tools envsubst
+SHELL ["/bin/bash", "-o", "pipefail", "-c"]
+
+# Copy only the final binaries from the AWG builder stages
+COPY --from=awg-go /usr/bin/amneziawg-go /usr/bin/amneziawg-go
+COPY --from=awg-tools /workspace/awg-tools/src/wg /usr/bin/awg
+COPY --from=awg-tools /workspace/awg-tools/src/wg-quick/linux.bash /usr/bin/awg-quick
+
+# Environment variables
+ARG wg_net="10.0.0.1"
+ARG wg_subn="24"
+ARG wg_port="51820"
+ENV TZ="Europe/Amsterdam" \
+ global_dns="9.9.9.9" \
+ wgd_port="10086" \
+ public_ip="" \
+ WGDASH=/opt/wgdashboard
+
+# Create directories needed for operation
+RUN mkdir /data /configs -p ${WGDASH}/src /etc/amnezia/amneziawg \
+ && echo "name_servers=${global_dns}" >> /etc/resolvconf.conf
+
+# Copy the venv and source files from local compiled locations or repos
+COPY ./src ${WGDASH}/src
+COPY --from=pip-builder /opt/wgdashboard/src/venv /opt/wgdashboard/src/venv
+COPY ./docker/templates/wg0.conf /tmp/wg0.conf.template
+COPY ./docker/templates/wgdashboard-oidc-providers.json /tmp/wg-dashboard-oidc-providers.json.template
+# Copy in the runtime script, essential.
+COPY ./docker/entrypoint.sh /entrypoint.sh
+
+# First WireGuard interface template
+RUN export out_adapt=$(ip -o -4 route show to default | awk '{print $NF}') \
+ && envsubst < /tmp/wg0.conf.template > /configs/wg0.conf.template \
+ && chmod 600 /configs/wg0.conf.template \
+ && cat /configs/wg0.conf.template
+
+# Set a healthcheck to determine the container its health
+HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
+ CMD sh -c 'pgrep gunicorn > /dev/null && pgrep tail > /dev/null' || exit 1
+
+# Expose ports on the container
+EXPOSE 10086
+WORKDIR $WGDASH/src
+
+ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 0000000..f32e8f4
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,217 @@
+# WGDashboard Docker Explanation:
+Author: @DaanSelen
+
+This document delves into how the WGDashboard Docker container has been built.
+Of course there are two stages (simply said), one before run-time and one at/after run-time.
+The `Dockerfile` describes how the container image is made, and the `entrypoint.sh` is executed after the container is started.
+In this example, [WireGuard](https://www.wireguard.com/) is integrated into the container itself, so it should be a run-and-go(/out-of-the-box) experience.
+For more details on the source-code specific to this Docker image, refer to the source files, they have lots of comments.
+
+
+
+
+
+To get the container running you either pull the pre-made image from a remote repository, there are 2 official options.
+
+- ghcr.io/wgdashboard/wgdashboard:
+- docker.io/donaldzou/wgdashboard:
+
+> tags should be either: latest, main, , (if built) or .
+
+From there either use the environment variables described below as parameters or use the Docker Compose file: `compose.yaml`.
+Be careful, the default generated WireGuard configuration file uses port 51820/udp. So make sure to use this port if you want to use it out of the box.
+Otherwise edit the configuration file in WGDashboard under `Configuration Settings` -> `Edit Raw Configuration File`.
+
+> Otherwise you need to enter the container and edit: `/etc/wireguard/wg0.conf`.
+
+# WGDashboard: 🐳 Docker Deployment Guide
+
+To run the container, you can either pull the image from the Github Container Registry (ghcr.io), Docker Hub (docker.io) or build it yourself. The image is available at:
+
+> `docker.io` is in most cases automatically resolved by the Docker application. Therefor you can ofter specify: `donaldzou/wgdashboard:latest`
+
+### 🔧 Quick Docker Run Command
+
+Here's an example to get it up and running quickly:
+
+```bash
+docker run -d \
+ --name wgdashboard \
+ --restart unless-stopped \
+ -p 10086:10086/tcp \
+ -p 51820:51820/udp \
+ --cap-add NET_ADMIN \
+ ghcr.io/wgdashboard/wgdashboard:latest
+```
+
+> ⚠️ The default WireGuard port is `51820/udp`. If you change this, update the `/etc/wireguard/wg0.conf` accordingly.
+
+---
+
+### 📦 Docker Compose Alternative (see the [compose file](./compose.yaml))
+
+You can also use Docker Compose for easier configuration:
+
+```yaml
+services:
+ wgdashboard:
+ image: ghcr.io/wgdashboard/wgdashboard:latest
+ restart: unless-stopped
+ container_name: wgdashboard
+ ports:
+ - 10086:10086/tcp
+ - 51820:51820/udp
+
+ volumes:
+ - aconf:/etc/amnezia/amneziawg
+ - conf:/etc/wireguard
+ - data:/data
+
+ cap_add:
+ - NET_ADMIN
+
+volumes:
+ aconf:
+ conf:
+ data:
+```
+
+> 📁 You can customize the **volume paths** on the host to fit your needs. The example above uses Docker volumes.
+
+---
+
+## 🔄 Updating the Container
+
+Updating the WGDashboard container should be through 'The Docker Way' - by pulling the newest/newer image and replacing this old one.
+
+---
+
+## ⚙️ Environment Variables
+
+| Variable | Accepted Values | Default | Example | Description |
+| ------------------ | ---------------------------------------- | ----------------------- | --------------------- | ----------------------------------------------------------------------- |
+| `tz` | Timezone | `Europe/Amsterdam` | `America/New_York` | Sets the container's timezone. Useful for accurate logs and scheduling. |
+| `global_dns` | IPv4 and IPv6 addresses | `9.9.9.9` | `8.8.8.8`, `1.1.1.1` | Default DNS for WireGuard clients. |
+| `public_ip` | Public IP address | Retrieved automatically | `253.162.134.73` | Used to generate accurate client configs. Needed if container is NAT’d. |
+| `wgd_port` | Any port that is allowed for the process | `10086` | `443` | This port is used to set the WGDashboard web port. |
+| `username` | Any non‐empty string | `-` | `admin` | Username for the WGDashboard web interface account. |
+| `password` | Any non‐empty string | `-` | `s3cr3tP@ss` | Password for the WGDashboard web interface account (stored hashed). |
+| `enable_totp` | `true`, `false` | `true` | `false` | Enable TOTP‐based two‐factor authentication for the account. |
+| `wg_autostart` | Wireguard interface name | `false` | `true` | Auto‐start the WireGuard client when the container launches. |
+| `email_server` | SMTP server address | `-` | `smtp.gmail.com` | SMTP server for sending email notifications. |
+| `email_port` | SMTP port number | `-` | `587` | Port for connecting to the SMTP server. |
+| `email_encryption` | `TLS`, `SSL`, etc. | `-` | `TLS` | Encryption method for email communication. |
+| `email_username` | Any non-empty string | `-` | `user@example.com` | Username for SMTP authentication. |
+| `email_password` | Any non-empty string | `-` | `app_password` | Password for SMTP authentication. |
+| `email_from` | Valid email address | `-` | `noreply@example.com` | Email address used as the sender for notifications. |
+| `email_template` | Path to template file | `-` | `your-template` | Custom template for email notifications. |
+| `database_type` | `sqlite`, `postgresql`, `mariadb+mariadbconnector`, etc. | `-` | `postgresql` | Type of [sqlalchemy database engine](https://docs.sqlalchemy.org/en/21/core/engines.html). |
+| `database_host` | Any non-empty string | `-` | `localhost` | IP-Address or hostname of the SQL-database server. |
+| `database_port` | Any non-empty string (or int for port) | `-` | `5432` | Port for the database communication. |
+| `database_username`| Valid database username | `-` | `database_user` | Database user username. |
+| `database_password`| Valid database password | `-` | `database_password` | Database user password. |
+
+---
+
+## 🔐 Port Forwarding Note
+
+When using multiple WireGuard interfaces, remember to **open their respective ports** on the host.
+
+Examples:
+```yaml
+# Individual mapping
+- 51821:51821/udp
+
+# Or port range
+- 51820-51830:51820-51830/udp
+```
+
+> 🚨 **Security Tip:** Only expose ports you actually use.
+
+---
+
+## 🛠️ Building the Image Yourself
+
+To build from source:
+
+```bash
+git clone https://github.com/WGDashboard/WGDashboard.git
+cd WGDashboard
+docker build . -f docker/Dockerfile -t yourname/wgdashboard:latest
+```
+
+Example output:
+```shell
+docker images
+
+REPOSITORY TAG IMAGE ID CREATED SIZE
+yourname/wgdashboard latest c96fd96ee3b3 42 minutes ago 314MB
+```
+
+---
+
+## 🧱 Dockerfile Overview
+
+Here's a brief overview of the Dockerfile stages used in the image build:
+
+### 1. **Build Tools & Go Compilation**
+
+```Dockerfile
+FROM golang:1.24 AS compiler
+WORKDIR /go
+
+RUN apt-get update && apt-get install -y ...
+RUN git clone ... && make
+...
+```
+
+### 2. **Binary Copy to Scratch**
+
+```Dockerfile
+FROM scratch AS bins
+COPY --from=compiler /go/amneziawg-go/amneziawg-go /amneziawg-go
+...
+```
+
+### 3. **Final Alpine Container Setup**
+
+```Dockerfile
+FROM alpine:latest
+COPY --from=bins ...
+RUN apk update && apk add --no-cache ...
+COPY ./src ${WGDASH}/src
+COPY ./docker/entrypoint.sh /entrypoint.sh
+...
+EXPOSE 10086
+ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
+```
+
+---
+
+## 🚀 Entrypoint Overview
+
+### Major Functions:
+
+- **`ensure_installation`**: Sets up the app, database, and Python environment.
+- **`set_envvars`**: Writes `wg-dashboard.ini` and applies environment variables.
+- **`start_core`**: Starts the main WGDashboard service.
+- **`ensure_blocking`**: Tails the error log to keep the container process alive.
+
+---
+
+## ✅ Final Notes
+
+- Use `docker logs wgdashboard` for troubleshooting.
+- Access the web interface via `http://your-ip:10086` (or whichever port you specified in the compose).
+- The first time run will auto-generate WireGuard keys and configs (configs are generated from the template).
+
+## Closing remarks:
+
+For feedback please submit an issue to the repository. Or message dselen@nerthus.nl.
diff --git a/docker/compose.yaml b/docker/compose.yaml
new file mode 100644
index 0000000..2c5f37a
--- /dev/null
+++ b/docker/compose.yaml
@@ -0,0 +1,43 @@
+services:
+ wgdashboard:
+ # Since the github organisation we recommend the ghcr.io.
+ # Alternatively we also still push to docker.io under donaldzou/wgdashboard.
+ # Both share the exact same tags. So they should be interchangable.
+ image: ghcr.io/wgdashboard/wgdashboard:v4.3.2-dev
+
+ # Make sure to set the restart policy. Because for a VPN its important to come back IF it crashes.
+ restart: unless-stopped
+ container_name: wgdashboard
+
+ # Environment variables can be used to configure certain values at startup. Without having to configure it from the dashboard.
+ # By default its all disabled, but uncomment the following lines to apply these. (uncommenting is removing the # character)
+ # Refer to the documentation on https://wgdashboard.dev/ for more info on what everything means.
+ #environment:
+ #- wg_autostart=wg0
+ #- tz= # <--- Set container timezone, default: Europe/Amsterdam.
+ #- public_ip= # <--- Set public IP to ensure the correct one is chosen, defaulting to the IP give by ifconfig.me.
+ #- wgd_port= # <--- Set the port WGDashboard will use for its web-server.
+
+ # The following section, ports is very important for exposing more than one Wireguard/AmneziaWireguard interfaces.
+ # Once you create a new configuration and assign a port in the dashboard, don't forget to add it to the ports as well.
+ # Quick-tip: most Wireguard VPN tunnels use UDP. WGDashboard uses HTTP, so tcp.
+ ports:
+ - 10087:10086/tcp
+ - 51820:51820/udp
+
+ # Volumes can be configured however you'd like. The default is using docker volumes.
+ # If you want to use local paths, replace the path before the : with your path.
+ volumes:
+ - aconf:/etc/amnezia/amneziawg
+ - conf:/etc/wireguard
+ - data:/data
+
+ # Needed for network administration.
+ cap_add:
+ - NET_ADMIN
+
+# The following configuration is linked to the above default volumes.
+volumes:
+ aconf:
+ conf:
+ data:
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
new file mode 100644
index 0000000..daef69c
--- /dev/null
+++ b/docker/entrypoint.sh
@@ -0,0 +1,329 @@
+#!/bin/bash
+
+config_file="/data/wg-dashboard.ini"
+runtime_pid=""
+
+trap 'stop_service' SIGTERM
+
+# Hash password with bcrypt
+hash_password() {
+ ${WGDASH}/src/venv/bin/python3 -c "import bcrypt; print(bcrypt.hashpw('$1'.encode(), bcrypt.gensalt(12)).decode())"
+}
+
+# Function to set or update section/key/value in the INI file
+set_ini() {
+ local section="$1" key="$2" value="$3"
+ local current_value
+
+ # Add section if it doesn't exist
+ grep -q "^\[${section}\]" "$config_file" \
+ || printf "\n[%s]\n" "${section}" >> "$config_file"
+
+ # Check current value if key exists
+ if grep -q "^[[:space:]]*${key}[[:space:]]*=" "$config_file"; then
+ current_value=$(grep "^[[:space:]]*${key}[[:space:]]*=" "$config_file" | cut -d= -f2- | xargs)
+
+ # Dont display actual value if it's a password field
+ if [[ "$key" == *"password"* ]]; then
+ if [ "$current_value" = "$value" ]; then
+ echo "- $key is already set correctly (value hidden)"
+ return 0
+ fi
+ sed -i "/^\[${section}\]/,/^\[/{s|^[[:space:]]*${key}[[:space:]]*=.*|${key} = ${value}|}" "$config_file"
+ echo "- Updated $key (value hidden)"
+ else
+ if [ "$current_value" = "$value" ]; then
+ echo "- $key is already set correctly ($value)"
+ return 0
+ fi
+ sed -i "/^\[${section}\]/,/^\[/{s|^[[:space:]]*${key}[[:space:]]*=.*|${key} = ${value}|}" "$config_file"
+ echo "- Updated $key to: $value"
+ fi
+ else
+ sed -i "/^\[${section}\]/a ${key} = ${value}" "$config_file"
+
+ # Don't display actual value if it's a password field
+ if [[ "$key" == *"password"* ]]; then
+ echo "- Added new setting $key (value hidden)"
+ else
+ echo "- Added new setting $key: $value"
+ fi
+ fi
+}
+
+stop_service() {
+ echo "[WGDashboard] Stopping WGDashboard..."
+
+ local max_rounds="10"
+ local round="0"
+ local runtime_pid=""
+
+ while true; do
+ round=$((round + 1))
+
+ if [[ -f ${WGDASH}/src/gunicorn.pid ]]; then
+ runtime_pid=$(cat ${WGDASH}/src/gunicorn.pid)
+
+ echo "Running as PID: ${runtime_pid}"
+ return 0
+ fi
+
+ if [[ $round -eq $max_rounds ]]; then
+ echo "Reached breaking point!"
+ return 1
+
+ fi
+
+ sleep 0.5s
+ done
+
+ kill $runtime_pid
+ exit 0
+}
+
+echo "------------------------- START ----------------------------"
+echo "Starting the WGDashboard Docker container."
+
+ensure_installation() {
+ echo "Quick-installing..."
+
+ # Make the wgd.sh script executable.
+ chmod +x "${WGDASH}"/src/wgd.sh
+ cd "${WGDASH}"/src || exit
+
+ # Github issue: https://github.com/donaldzou/WGDashboard/issues/723
+ echo "Checking for stale pids..."
+ if [[ -f ${WGDASH}/src/gunicorn.pid ]]; then
+ echo "Found stale pid, removing..."
+ rm ${WGDASH}/src/gunicorn.pid
+ fi
+
+ # Removing clear shell command from the wgd.sh script to enhance docker logging.
+ echo "Removing clear command from wgd.sh for better Docker logging."
+ sed -i '/clear/d' ./wgd.sh
+
+ # PERSISTENCE FOR databases directory
+ # Create required directories and links
+ if [ ! -d "/data/db" ]; then
+ echo "Creating database dir"
+ mkdir -p /data/db
+ fi
+
+ if [[ ! -L "${WGDASH}/src/db" ]] && [[ -d "${WGDASH}/src/db" ]]; then
+ echo "Removing ${WGDASH}/src/db since its not a symbolic link."
+ rm -rfv "${WGDASH}/src/db"
+ fi
+ if [[ -L "${WGDASH}/src/db" ]]; then
+ echo "${WGDASH}/src/db is a symbolic link."
+ else
+ ln -sv /data/db "${WGDASH}/src/db"
+ fi
+
+ # PERSISTENCE FOR wg-dashboard-oidc-providers.json
+ if [ ! -f "/data/wg-dashboard-oidc-providers.json" ]; then
+ echo "Creating wg-dashboard-oidc-providers.json file"
+ cp -v /tmp/wg-dashboard-oidc-providers.json.template /data/wg-dashboard-oidc-providers.json
+ fi
+ if [[ ! -L "${WGDASH}/src/wg-dashboard-oidc-providers.json" ]] && [[ -f "${WGDASH}/src/wg-dashboard-oidc-providers.json" ]]; then
+ echo "Removing ${WGDASH}/src/wg-dashboard-oidc-providers.json since its not a symbolic link."
+ rm -fv "${WGDASH}/src/wg-dashboard-oidc-providers.json"
+ fi
+ if [[ -L "${WGDASH}/src/wg-dashboard-oidc-providers.json" ]]; then
+ echo "${WGDASH}/src/wg-dashboard-oidc-providers.json is a symbolic link."
+ else
+ ln -sv /data/wg-dashboard-oidc-providers.json "${WGDASH}/src/wg-dashboard-oidc-providers.json"
+ fi
+
+ # PERSISTENCE FOR wg-dashboard.ini
+ if [ ! -f "${config_file}" ]; then
+ echo "Creating wg-dashboard.ini file"
+ touch "${config_file}"
+ fi
+ if [[ ! -L "${WGDASH}/src/wg-dashboard.ini" ]] && [[ -f "${WGDASH}/src/wg-dashboard.ini" ]]; then
+ echo "Removing ${WGDASH}/src/wg-dashboard.ini since its not a symbolic link."
+ rm -fv "${WGDASH}/src/wg-dashboard.ini"
+ fi
+ if [[ -L "${WGDASH}/src/wg-dashboard.ini" ]]; then
+ echo "${WGDASH}/src/wg-dashboard.ini is a symbolic link."
+ else
+ ln -sv "${config_file}" "${WGDASH}/src/wg-dashboard.ini"
+ fi
+
+ # Setup WireGuard if needed
+ if [ -z "$(ls -A /etc/wireguard)" ]; then
+ cp -a "/configs/wg0.conf.template" "/etc/wireguard/wg0.conf"
+
+ echo "Setting a secure private key."
+ local privateKey
+ privateKey=$(wg genkey)
+ sed -i "s|^PrivateKey *=.*$|PrivateKey = ${privateKey}|g" /etc/wireguard/wg0.conf
+
+ echo "Done setting template."
+ else
+ echo "Existing wg0 configuration file found, using that."
+ fi
+}
+
+set_envvars() {
+ printf "\n------------- SETTING ENVIRONMENT VARIABLES ----------------\n"
+
+ # Check if config file is empty
+ if [ ! -s "${config_file}" ]; then
+ echo "Config file is empty. Creating initial structure."
+ fi
+
+ echo "Checking basic configuration:"
+ set_ini Peers peer_global_dns "${global_dns}"
+
+ if [ -z "${public_ip}" ]; then
+ public_ip=$(curl -s https://ifconfig.me)
+ if [ -z "${public_ip}" ]; then
+ echo "Using fallback public IP resolution website"
+ public_ip=$(curl -s https://api.ipify.org)
+ fi
+ if [ -z "${public_ip}" ]; then
+ echo "Failed to resolve publicly. Using private address."
+ public_ip=$(hostname -i)
+ fi
+ echo "Automatically detected public IP: ${public_ip}"
+ fi
+
+ set_ini Peers remote_endpoint "${public_ip}"
+ set_ini Server app_port "${wgd_port}"
+
+ # Account settings - process all parameters
+ [[ -n "$username" ]] && echo "Configuring user account:"
+ # Basic account variables
+ [[ -n "$username" ]] && set_ini Account username "${username}"
+
+ if [[ -n "$password" ]]; then
+ echo "- Setting password"
+ set_ini Account password "$(hash_password "${password}")"
+ fi
+
+ # Additional account variables
+ [[ -n "$enable_totp" ]] && set_ini Account enable_totp "${enable_totp}"
+ [[ -n "$totp_verified" ]] && set_ini Account totp_verified "${totp_verified}"
+ [[ -n "$totp_key" ]] && set_ini Account totp_key "${totp_key}"
+
+ # Welcome session
+ [[ -n "$welcome_session" ]] && set_ini Other welcome_session "${welcome_session}"
+ # If username and password are set but welcome_session isn't, disable it
+ if [[ -n "$username" && -n "$password" && -z "$welcome_session" ]]; then
+ set_ini Other welcome_session "false"
+ fi
+
+ # Autostart WireGuard
+ if [[ -n "$wg_autostart" ]]; then
+ echo "Configuring WireGuard autostart:"
+ set_ini WireGuardConfiguration autostart "${wg_autostart}"
+ fi
+
+ # Database (check if any settings need to be configured)
+ database_vars=("database_type" "database_host" "database_port" "database_username" "database_password")
+ for var in "${database_vars[@]}"; do
+ if [ -n "${!var}" ]; then
+ echo "Configuring database settings:"
+ break
+ fi
+ done
+
+ # Database (iterate through all possible fields)
+ database_fields=("type:database_type" "host:database_host" "port:database_port"
+ "username:database_username" "password:database_password")
+
+ for field_pair in "${database_fields[@]}"; do
+ IFS=: read -r field var <<< "$field_pair"
+ [[ -n "${!var}" ]] && set_ini Database "$field" "${!var}"
+ done
+
+ # Email (check if any settings need to be configured)
+ email_vars=("email_server" "email_port" "email_encryption" "email_username" "email_password" "email_from" "email_template")
+ for var in "${email_vars[@]}"; do
+ if [ -n "${!var}" ]; then
+ echo "Configuring email settings:"
+ break
+ fi
+ done
+
+ # Email (iterate through all possible fields)
+ email_fields=("server:email_server" "port:email_port" "encryption:email_encryption"
+ "username:email_username" "email_password:email_password"
+ "send_from:email_from" "email_template:email_template")
+
+ for field_pair in "${email_fields[@]}"; do
+ IFS=: read -r field var <<< "$field_pair"
+ [[ -n "${!var}" ]] && set_ini Email "$field" "${!var}"
+ done
+}
+
+# Start service and monitor logs
+start_and_monitor() {
+ printf "\n---------------------- STARTING CORE -----------------------\n"
+
+ # Due to resolvconf resetting the DNS we echo back the one we defined (or fallback to default).
+ resolvconf -u
+
+ # Due to some instances complaining about this, making sure its there every time.
+ mkdir -p /dev/net
+ mknod /dev/net/tun c 10 200
+ chmod 600 /dev/net/tun
+
+ # Actually starting WGDashboard
+ echo "Starting WGDashboard directly with Gunicorn..."
+
+ [[ ! -d ${WGDASH}/src/log ]] && mkdir ${WGDASH}/src/log
+ [[ ! -d ${WGDASH}/src/download ]] && mkdir ${WGDASH}/src/download
+
+ ${WGDASH}/src/venv/bin/gunicorn --config ${WGDASH}/src/gunicorn.conf.py
+
+ if [ $? -ne 0 ]; then
+ echo "Loading WGDashboard failed... Look above for details."
+ fi
+
+ # Wait a second before continuing, to give the python program some time to get ready.
+ echo -e "\nEnsuring container continuation."
+
+ local max_rounds="10"
+ local round="0"
+
+ # Hang in there for 10s for Gunicorn to get ready
+ while true; do
+ round=$((round + 1))
+
+ local latest_error=$(ls -t ${WGDASH}/src/log/error_*.log 2> /dev/null | head -n 1)
+
+ if [[ $round -eq $max_rounds ]]; then
+ echo "Reached breaking point!"
+ break
+
+ fi
+
+ if [[ -z $latest_error ]]; then
+ echo -e "Logs not yet present! Retrying in 1 second!"
+ sleep 1s
+
+ else
+ break
+
+ fi
+
+ done
+
+ if [[ -z $latest_error ]]; then
+ echo -e "No error logs founds... Please investigate.\nExiting in 3 minutes..."
+ sleep 180s
+ exit 1
+
+ else
+ tail -f "$latest_error" &
+ tail_pid=$!
+
+ wait $tail_pid
+ fi
+}
+
+# Main execution flow
+ensure_installation
+set_envvars
+start_and_monitor
diff --git a/docker/templates/wg0.conf b/docker/templates/wg0.conf
new file mode 100644
index 0000000..4d7779c
--- /dev/null
+++ b/docker/templates/wg0.conf
@@ -0,0 +1,8 @@
+[Interface]
+Address = ${wg_net}/24
+PrivateKey =
+PostUp = iptables -t nat -I POSTROUTING 1 -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE; iptables -I FORWARD -i wg0 -o wg0 -j DROP
+PreDown = iptables -t nat -D POSTROUTING -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE; iptables -D FORWARD -i wg0 -o wg0 -j DROP
+ListenPort = ${wg_port}
+SaveConfig = true
+DNS = ${global_dns}
diff --git a/docker/templates/wgdashboard-oidc-providers.json b/docker/templates/wgdashboard-oidc-providers.json
new file mode 100644
index 0000000..3764d0d
--- /dev/null
+++ b/docker/templates/wgdashboard-oidc-providers.json
@@ -0,0 +1,16 @@
+{
+ "Admin": {
+ "Provider": {
+ "client_id": "",
+ "client_secret": "",
+ "issuer": ""
+ }
+ },
+ "Client": {
+ "Provider": {
+ "client_id": "",
+ "client_secret": "",
+ "issuer": ""
+ }
+ }
+}
diff --git a/src/static/client/src/components/Configuration/configuration.vue b/src/static/client/src/components/Configuration/configuration.vue
index f146a5d..29fca2d 100644
--- a/src/static/client/src/components/Configuration/configuration.vue
+++ b/src/static/client/src/components/Configuration/configuration.vue
@@ -2,8 +2,8 @@
import {computed, ref} from "vue";
import ConfigurationQRCode from "@/components/Configuration/configurationQRCode.vue";
import dayjs from "dayjs";
-import Duration from 'dayjs/plugin/Duration'
-dayjs.extend(Duration);
+import duration from 'dayjs/plugin/duration'
+dayjs.extend(duration);
const props = defineProps([
'config'
])
@@ -113,4 +113,4 @@ const emits = defineEmits(['select'])
background-color: #28a745 !important;
box-shadow: 0 0 0 .2rem #28a74545;
}
-
\ No newline at end of file
+