Compare commits

..

2 Commits

Author SHA1 Message Date
thelamer
8c28cb7a40 make the env proxy confs their own isolated folder to include 2025-09-04 15:28:25 -04:00
thelamer
5942cc2253 initial env var ingestion for rev proxy configs 2025-09-02 15:16:05 -04:00
18 changed files with 818 additions and 283 deletions

View File

@@ -79,6 +79,8 @@ RUN \
php84-tokenizer \ php84-tokenizer \
php84-xmlreader \ php84-xmlreader \
php84-xsl \ php84-xsl \
python3 \
py3-jinja2 \
whois && \ whois && \
echo "**** install certbot plugins ****" && \ echo "**** install certbot plugins ****" && \
if [ -z ${CERTBOT_VERSION+x} ]; then \ if [ -z ${CERTBOT_VERSION+x} ]; then \
@@ -114,7 +116,6 @@ RUN \
certbot-dns-google \ certbot-dns-google \
certbot-dns-he \ certbot-dns-he \
certbot-dns-hetzner \ certbot-dns-hetzner \
certbot-dns-hetzner-cloud \
certbot-dns-infomaniak \ certbot-dns-infomaniak \
certbot-dns-inwx \ certbot-dns-inwx \
certbot-dns-ionos \ certbot-dns-ionos \

View File

@@ -79,6 +79,8 @@ RUN \
php84-tokenizer \ php84-tokenizer \
php84-xmlreader \ php84-xmlreader \
php84-xsl \ php84-xsl \
python3 \
py3-jinja2 \
whois && \ whois && \
echo "**** install certbot plugins ****" && \ echo "**** install certbot plugins ****" && \
if [ -z ${CERTBOT_VERSION+x} ]; then \ if [ -z ${CERTBOT_VERSION+x} ]; then \
@@ -114,7 +116,6 @@ RUN \
certbot-dns-google \ certbot-dns-google \
certbot-dns-he \ certbot-dns-he \
certbot-dns-hetzner \ certbot-dns-hetzner \
certbot-dns-hetzner-cloud \
certbot-dns-infomaniak \ certbot-dns-infomaniak \
certbot-dns-inwx \ certbot-dns-inwx \
certbot-dns-ionos \ certbot-dns-ionos \

110
Jenkinsfile vendored
View File

@@ -208,7 +208,6 @@ pipeline {
env.META_TAG = env.EXT_RELEASE_CLEAN + '-ls' + env.LS_TAG_NUMBER env.META_TAG = env.EXT_RELEASE_CLEAN + '-ls' + env.LS_TAG_NUMBER
env.EXT_RELEASE_TAG = 'version-' + env.EXT_RELEASE_CLEAN env.EXT_RELEASE_TAG = 'version-' + env.EXT_RELEASE_CLEAN
env.BUILDCACHE = 'docker.io/lsiodev/buildcache,registry.gitlab.com/linuxserver.io/docker-jenkins-builder/lsiodev-buildcache,ghcr.io/linuxserver/lsiodev-buildcache,quay.io/linuxserver.io/lsiodev-buildcache' env.BUILDCACHE = 'docker.io/lsiodev/buildcache,registry.gitlab.com/linuxserver.io/docker-jenkins-builder/lsiodev-buildcache,ghcr.io/linuxserver/lsiodev-buildcache,quay.io/linuxserver.io/lsiodev-buildcache'
env.CITEST_IMAGETAG = 'latest'
} }
} }
} }
@@ -234,7 +233,6 @@ pipeline {
env.EXT_RELEASE_TAG = 'version-' + env.EXT_RELEASE_CLEAN env.EXT_RELEASE_TAG = 'version-' + env.EXT_RELEASE_CLEAN
env.DOCKERHUB_LINK = 'https://hub.docker.com/r/' + env.DEV_DOCKERHUB_IMAGE + '/tags/' env.DOCKERHUB_LINK = 'https://hub.docker.com/r/' + env.DEV_DOCKERHUB_IMAGE + '/tags/'
env.BUILDCACHE = 'docker.io/lsiodev/buildcache,registry.gitlab.com/linuxserver.io/docker-jenkins-builder/lsiodev-buildcache,ghcr.io/linuxserver/lsiodev-buildcache,quay.io/linuxserver.io/lsiodev-buildcache' env.BUILDCACHE = 'docker.io/lsiodev/buildcache,registry.gitlab.com/linuxserver.io/docker-jenkins-builder/lsiodev-buildcache,ghcr.io/linuxserver/lsiodev-buildcache,quay.io/linuxserver.io/lsiodev-buildcache'
env.CITEST_IMAGETAG = 'develop'
} }
} }
} }
@@ -260,7 +258,6 @@ pipeline {
env.CODE_URL = 'https://github.com/' + env.LS_USER + '/' + env.LS_REPO + '/pull/' + env.PULL_REQUEST env.CODE_URL = 'https://github.com/' + env.LS_USER + '/' + env.LS_REPO + '/pull/' + env.PULL_REQUEST
env.DOCKERHUB_LINK = 'https://hub.docker.com/r/' + env.PR_DOCKERHUB_IMAGE + '/tags/' env.DOCKERHUB_LINK = 'https://hub.docker.com/r/' + env.PR_DOCKERHUB_IMAGE + '/tags/'
env.BUILDCACHE = 'docker.io/lsiodev/buildcache,registry.gitlab.com/linuxserver.io/docker-jenkins-builder/lsiodev-buildcache,ghcr.io/linuxserver/lsiodev-buildcache,quay.io/linuxserver.io/lsiodev-buildcache' env.BUILDCACHE = 'docker.io/lsiodev/buildcache,registry.gitlab.com/linuxserver.io/docker-jenkins-builder/lsiodev-buildcache,ghcr.io/linuxserver/lsiodev-buildcache,quay.io/linuxserver.io/lsiodev-buildcache'
env.CITEST_IMAGETAG = 'develop'
} }
} }
} }
@@ -283,7 +280,7 @@ pipeline {
-v ${WORKSPACE}:/mnt \ -v ${WORKSPACE}:/mnt \
-e AWS_ACCESS_KEY_ID=\"${S3_KEY}\" \ -e AWS_ACCESS_KEY_ID=\"${S3_KEY}\" \
-e AWS_SECRET_ACCESS_KEY=\"${S3_SECRET}\" \ -e AWS_SECRET_ACCESS_KEY=\"${S3_SECRET}\" \
ghcr.io/linuxserver/baseimage-alpine:3.23 s6-envdir -fn -- /var/run/s6/container_environment /bin/bash -c "\ ghcr.io/linuxserver/baseimage-alpine:3.20 s6-envdir -fn -- /var/run/s6/container_environment /bin/bash -c "\
apk add --no-cache python3 && \ apk add --no-cache python3 && \
python3 -m venv /lsiopy && \ python3 -m venv /lsiopy && \
pip install --no-cache-dir -U pip && \ pip install --no-cache-dir -U pip && \
@@ -618,16 +615,13 @@ pipeline {
echo $GITHUB_TOKEN | docker login ghcr.io -u LinuxServer-CI --password-stdin echo $GITHUB_TOKEN | docker login ghcr.io -u LinuxServer-CI --password-stdin
echo $GITLAB_TOKEN | docker login registry.gitlab.com -u LinuxServer.io --password-stdin echo $GITLAB_TOKEN | docker login registry.gitlab.com -u LinuxServer.io --password-stdin
echo $QUAYPASS | docker login quay.io -u $QUAYUSER --password-stdin echo $QUAYPASS | docker login quay.io -u $QUAYUSER --password-stdin
if [[ "${PACKAGE_CHECK}" != "true" ]]; then if [[ "${PACKAGE_CHECK}" != "true" ]]; then
declare -A pids
IFS=',' read -ra CACHE <<< "$BUILDCACHE" IFS=',' read -ra CACHE <<< "$BUILDCACHE"
for i in "${CACHE[@]}"; do for i in "${CACHE[@]}"; do
docker push ${i}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} & docker push ${i}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} &
pids[$!]="$i"
done done
for p in "${!pids[@]}"; do for p in $(jobs -p); do
wait "$p" || { [[ "${pids[$p]}" != *"quay.io"* ]] && exit 1; } wait "$p" || { echo "job $p failed" >&2; exit 1; }
done done
fi fi
''' '''
@@ -687,16 +681,13 @@ pipeline {
echo $GITHUB_TOKEN | docker login ghcr.io -u LinuxServer-CI --password-stdin echo $GITHUB_TOKEN | docker login ghcr.io -u LinuxServer-CI --password-stdin
echo $GITLAB_TOKEN | docker login registry.gitlab.com -u LinuxServer.io --password-stdin echo $GITLAB_TOKEN | docker login registry.gitlab.com -u LinuxServer.io --password-stdin
echo $QUAYPASS | docker login quay.io -u $QUAYUSER --password-stdin echo $QUAYPASS | docker login quay.io -u $QUAYUSER --password-stdin
if [[ "${PACKAGE_CHECK}" != "true" ]]; then if [[ "${PACKAGE_CHECK}" != "true" ]]; then
declare -A pids
IFS=',' read -ra CACHE <<< "$BUILDCACHE" IFS=',' read -ra CACHE <<< "$BUILDCACHE"
for i in "${CACHE[@]}"; do for i in "${CACHE[@]}"; do
docker push ${i}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} & docker push ${i}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} &
pids[$!]="$i"
done done
for p in "${!pids[@]}"; do for p in $(jobs -p); do
wait "$p" || { [[ "${pids[$p]}" != *"quay.io"* ]] && exit 1; } wait "$p" || { echo "job $p failed" >&2; exit 1; }
done done
fi fi
''' '''
@@ -750,14 +741,12 @@ pipeline {
echo $GITLAB_TOKEN | docker login registry.gitlab.com -u LinuxServer.io --password-stdin echo $GITLAB_TOKEN | docker login registry.gitlab.com -u LinuxServer.io --password-stdin
echo $QUAYPASS | docker login quay.io -u $QUAYUSER --password-stdin echo $QUAYPASS | docker login quay.io -u $QUAYUSER --password-stdin
if [[ "${PACKAGE_CHECK}" != "true" ]]; then if [[ "${PACKAGE_CHECK}" != "true" ]]; then
declare -A pids
IFS=',' read -ra CACHE <<< "$BUILDCACHE" IFS=',' read -ra CACHE <<< "$BUILDCACHE"
for i in "${CACHE[@]}"; do for i in "${CACHE[@]}"; do
docker push ${i}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} & docker push ${i}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} &
pids[$!]="$i"
done done
for p in "${!pids[@]}"; do for p in $(jobs -p); do
wait "$p" || { [[ "${pids[$p]}" != *"quay.io"* ]] && exit 1; } wait "$p" || { echo "job $p failed" >&2; exit 1; }
done done
fi fi
''' '''
@@ -882,7 +871,7 @@ pipeline {
CI_DOCKERENV="LSIO_FIRST_PARTY=true" CI_DOCKERENV="LSIO_FIRST_PARTY=true"
fi fi
fi fi
docker pull ghcr.io/linuxserver/ci:${CITEST_IMAGETAG} docker pull ghcr.io/linuxserver/ci:latest
if [ "${MULTIARCH}" == "true" ]; then if [ "${MULTIARCH}" == "true" ]; then
docker pull ghcr.io/linuxserver/lsiodev-buildcache:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} --platform=arm64 docker pull ghcr.io/linuxserver/lsiodev-buildcache:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} --platform=arm64
docker tag ghcr.io/linuxserver/lsiodev-buildcache:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} ${IMAGE}:arm64v8-${META_TAG} docker tag ghcr.io/linuxserver/lsiodev-buildcache:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} ${IMAGE}:arm64v8-${META_TAG}
@@ -906,9 +895,7 @@ pipeline {
-e WEB_PATH=\"${CI_WEBPATH}\" \ -e WEB_PATH=\"${CI_WEBPATH}\" \
-e NODE_NAME=\"${NODE_NAME}\" \ -e NODE_NAME=\"${NODE_NAME}\" \
-e SYFT_IMAGE_TAG=\"${CI_SYFT_IMAGE_TAG:-${SYFT_IMAGE_TAG}}\" \ -e SYFT_IMAGE_TAG=\"${CI_SYFT_IMAGE_TAG:-${SYFT_IMAGE_TAG}}\" \
-e COMMIT_SHA=\"${COMMIT_SHA}\" \ -t ghcr.io/linuxserver/ci:latest \
-e BUILD_NUMBER=\"${BUILD_NUMBER}\" \
-t ghcr.io/linuxserver/ci:${CITEST_IMAGETAG} \
python3 test_build.py''' python3 test_build.py'''
} }
} }
@@ -934,11 +921,9 @@ pipeline {
CACHEIMAGE=${i} CACHEIMAGE=${i}
fi fi
done done
docker buildx imagetools create --prefer-index=false -t ${PUSHIMAGE}:${META_TAG} -t ${PUSHIMAGE}:latest -t ${PUSHIMAGE}:${EXT_RELEASE_TAG} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ docker buildx imagetools create --prefer-index=false -t ${PUSHIMAGE}:${META_TAG} -t ${PUSHIMAGE}:latest -t ${PUSHIMAGE}:${EXT_RELEASE_TAG} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER}
{ if [[ "${PUSHIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; }
if [ -n "${SEMVER}" ]; then if [ -n "${SEMVER}" ]; then
docker buildx imagetools create --prefer-index=false -t ${PUSHIMAGE}:${SEMVER} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ docker buildx imagetools create --prefer-index=false -t ${PUSHIMAGE}:${SEMVER} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER}
{ if [[ "${PUSHIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; }
fi fi
done done
''' '''
@@ -963,27 +948,20 @@ pipeline {
CACHEIMAGE=${i} CACHEIMAGE=${i}
fi fi
done done
docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:amd64-${META_TAG} -t ${MANIFESTIMAGE}:amd64-latest -t ${MANIFESTIMAGE}:amd64-${EXT_RELEASE_TAG} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:amd64-${META_TAG} -t ${MANIFESTIMAGE}:amd64-latest -t ${MANIFESTIMAGE}:amd64-${EXT_RELEASE_TAG} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER}
{ if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:arm64v8-${META_TAG} -t ${MANIFESTIMAGE}:arm64v8-latest -t ${MANIFESTIMAGE}:arm64v8-${EXT_RELEASE_TAG} ${CACHEIMAGE}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER}
docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:arm64v8-${META_TAG} -t ${MANIFESTIMAGE}:arm64v8-latest -t ${MANIFESTIMAGE}:arm64v8-${EXT_RELEASE_TAG} ${CACHEIMAGE}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} || \
{ if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; }
if [ -n "${SEMVER}" ]; then if [ -n "${SEMVER}" ]; then
docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:amd64-${SEMVER} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:amd64-${SEMVER} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER}
{ if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:arm64v8-${SEMVER} ${CACHEIMAGE}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER}
docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:arm64v8-${SEMVER} ${CACHEIMAGE}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} || \
{ if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; }
fi fi
done done
for MANIFESTIMAGE in "${IMAGE}" "${GITLABIMAGE}" "${GITHUBIMAGE}" "${QUAYIMAGE}"; do for MANIFESTIMAGE in "${IMAGE}" "${GITLABIMAGE}" "${GITHUBIMAGE}" "${QUAYIMAGE}"; do
docker buildx imagetools create -t ${MANIFESTIMAGE}:latest ${MANIFESTIMAGE}:amd64-latest ${MANIFESTIMAGE}:arm64v8-latest || \ docker buildx imagetools create -t ${MANIFESTIMAGE}:latest ${MANIFESTIMAGE}:amd64-latest ${MANIFESTIMAGE}:arm64v8-latest
{ if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } docker buildx imagetools create -t ${MANIFESTIMAGE}:${META_TAG} ${MANIFESTIMAGE}:amd64-${META_TAG} ${MANIFESTIMAGE}:arm64v8-${META_TAG}
docker buildx imagetools create -t ${MANIFESTIMAGE}:${META_TAG} ${MANIFESTIMAGE}:amd64-${META_TAG} ${MANIFESTIMAGE}:arm64v8-${META_TAG} || \
{ if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; } docker buildx imagetools create -t ${MANIFESTIMAGE}:${EXT_RELEASE_TAG} ${MANIFESTIMAGE}:amd64-${EXT_RELEASE_TAG} ${MANIFESTIMAGE}:arm64v8-${EXT_RELEASE_TAG}
docker buildx imagetools create -t ${MANIFESTIMAGE}:${EXT_RELEASE_TAG} ${MANIFESTIMAGE}:amd64-${EXT_RELEASE_TAG} ${MANIFESTIMAGE}:arm64v8-${EXT_RELEASE_TAG} || \
{ if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; }
if [ -n "${SEMVER}" ]; then if [ -n "${SEMVER}" ]; then
docker buildx imagetools create -t ${MANIFESTIMAGE}:${SEMVER} ${MANIFESTIMAGE}:amd64-${SEMVER} ${MANIFESTIMAGE}:arm64v8-${SEMVER} || \ docker buildx imagetools create -t ${MANIFESTIMAGE}:${SEMVER} ${MANIFESTIMAGE}:amd64-${SEMVER} ${MANIFESTIMAGE}:arm64v8-${SEMVER}
{ if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" ]]; then exit 1; fi; }
fi fi
done done
''' '''
@@ -1001,41 +979,23 @@ pipeline {
environment name: 'EXIT_STATUS', value: '' environment name: 'EXIT_STATUS', value: ''
} }
steps { steps {
echo "Pushing New tag for current commit ${META_TAG}"
sh '''curl -H "Authorization: token ${GITHUB_TOKEN}" -X POST https://api.github.com/repos/${LS_USER}/${LS_REPO}/git/tags \
-d '{"tag":"'${META_TAG}'",\
"object": "'${COMMIT_SHA}'",\
"message": "Tagging Release '${EXT_RELEASE_CLEAN}'-ls'${LS_TAG_NUMBER}' to master",\
"type": "commit",\
"tagger": {"name": "LinuxServer-CI","email": "ci@linuxserver.io","date": "'${GITHUB_DATE}'"}}' '''
echo "Pushing New release for Tag"
sh '''#! /bin/bash sh '''#! /bin/bash
echo "Auto-generating release notes"
if [ "$(git tag --points-at HEAD)" != "" ]; then
echo "Existing tag points to current commit, suggesting no new LS changes"
AUTO_RELEASE_NOTES="No changes"
else
AUTO_RELEASE_NOTES=$(curl -fsL -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" -X POST https://api.github.com/repos/${LS_USER}/${LS_REPO}/releases/generate-notes \
-d '{"tag_name":"'${META_TAG}'",\
"target_commitish": "master"}' \
| jq -r '.body' | sed 's|## What.s Changed||')
fi
echo "Pushing New tag for current commit ${META_TAG}"
curl -H "Authorization: token ${GITHUB_TOKEN}" -X POST https://api.github.com/repos/${LS_USER}/${LS_REPO}/git/tags \
-d '{"tag":"'${META_TAG}'",\
"object": "'${COMMIT_SHA}'",\
"message": "Tagging Release '${EXT_RELEASE_CLEAN}'-ls'${LS_TAG_NUMBER}' to master",\
"type": "commit",\
"tagger": {"name": "LinuxServer-CI","email": "ci@linuxserver.io","date": "'${GITHUB_DATE}'"}}'
echo "Pushing New release for Tag"
echo "Updating PIP version of ${EXT_PIP} to ${EXT_RELEASE_CLEAN}" > releasebody.json echo "Updating PIP version of ${EXT_PIP} to ${EXT_RELEASE_CLEAN}" > releasebody.json
jq -n \ echo '{"tag_name":"'${META_TAG}'",\
--arg tag_name "$META_TAG" \ "target_commitish": "master",\
--arg target_commitish "master" \ "name": "'${META_TAG}'",\
--arg ci_url "${CI_URL:-N/A}" \ "body": "**CI Report:**\\n\\n'${CI_URL:-N/A}'\\n\\n**LinuxServer Changes:**\\n\\n'${LS_RELEASE_NOTES}'\\n\\n**Remote Changes:**\\n\\n' > start
--arg ls_notes "$AUTO_RELEASE_NOTES" \ printf '","draft": false,"prerelease": false}' >> releasebody.json
--arg remote_notes "$(cat releasebody.json)" \ paste -d'\\0' start releasebody.json > releasebody.json.done
'{ curl -H "Authorization: token ${GITHUB_TOKEN}" -X POST https://api.github.com/repos/${LS_USER}/${LS_REPO}/releases -d @releasebody.json.done'''
"tag_name": $tag_name,
"target_commitish": $target_commitish,
"name": $tag_name,
"body": ("**CI Report:**\\n\\n" + $ci_url + "\\n\\n**LinuxServer Changes:**\\n\\n" + $ls_notes + "\\n\\n**Remote Changes:**\\n\\n" + $remote_notes),
"draft": false,
"prerelease": false }' > releasebody.json.done
curl -H "Authorization: token ${GITHUB_TOKEN}" -X POST https://api.github.com/repos/${LS_USER}/${LS_REPO}/releases -d @releasebody.json.done
'''
} }
} }
// Add protection to the release branch // Add protection to the release branch

View File

@@ -85,6 +85,88 @@ INSTALL_PIP_PACKAGES=certbot-dns-<plugin>
Set the required credentials (usually found in the plugin documentation) in `/config/dns-conf/<plugin>.ini`. Set the required credentials (usually found in the plugin documentation) in `/config/dns-conf/<plugin>.ini`.
It is recommended to attempt obtaining a certificate with `STAGING=true` first to make sure the plugin is working as expected. It is recommended to attempt obtaining a certificate with `STAGING=true` first to make sure the plugin is working as expected.
### Dynamic Reverse Proxy Configuration via Environment Variables
SWAG can dynamically generate reverse proxy configuration files directly from environment variables, bypassing the need to manage individual `.conf` files. When any `PROXY_CONFIG_*` variable is detected, this mode is activated, and any existing `.conf` files in `/config/nginx/proxy-confs/` will be removed at startup.
**Service Definition**
Each reverse proxy service is defined by an environment variable following the format `PROXY_CONFIG_<SERVICE_NAME>`. The service name will be used as the subdomain (e.g., `SERVICE_NAME.yourdomain.com`), with the special exception of `DEFAULT` (see below). The value of the variable must be a valid JSON object.
```yaml
environment:
# Configure the default site (root domain) to proxy to a dashboard service
- 'PROXY_CONFIG_DEFAULT={"name": "dashboard", "port": 80, "auth": "authelia", "quic": true}'
# Simple subdomain service
- 'PROXY_CONFIG_HOMARR={"port": 7575, "auth": "authelia"}'
# Service with a boolean flag for HTTPS backend and QUIC enabled
- 'PROXY_CONFIG_HEIMDALL={"port": 443, "https": true, "quic": true}'
# Complex service with nested objects and lists (incomplete example for syntax)
- 'PROXY_CONFIG_PLEX={
"port": 32400,
"proxy_redirect_off": true,
"buffering_off": true,
"proxy_set_headers": [
{"key": "X-Plex-Client-Identifier", "value": "$$http_x_plex_client_identifier"},
{"key": "X-Plex-Device", "value": "$$http_x_plex_device"}
],
"extra_locations": [
{"path": "/library/streams/", "custom_directives": ["proxy_pass_request_headers off"]}
]
}'
```
The available keys in the JSON object correspond to the options in the underlying Nginx template. Common keys include `port`, `https`, `quic`, `auth`, `buffering_off`, `proxy_set_headers`, and `extra_locations`.
**Configuring the Default Site (Root Domain)**
To configure the service that responds on your root domain (e.g., `https://yourdomain.com`), use the special service name `DEFAULT`.
* The environment variable is `PROXY_CONFIG_DEFAULT`.
* Unlike subdomain services, the `DEFAULT` configuration **must** include a `"name"` key in its JSON value. This key specifies the name of the container that SWAG should proxy traffic to.
* If `PROXY_CONFIG_DEFAULT` is not set, the container will serve the standard SWAG welcome page on the root domain.
Example:
```yaml
environment:
# This will proxy https://yourdomain.com to the 'dashboard' container on port 80
- 'PROXY_CONFIG_DEFAULT={"name": "dashboard", "port": 80, "auth": "none"}'
```
**Authentication Management**
Authentication can be managed globally or per-service with a clear order of precedence.
1. **Per-Service Override (Highest Priority):** Add an `auth` key directly inside the service's JSON configuration.
* `"auth": "authelia"`: Enables Authelia for this service.
* `"auth": "basic"`: Enables Basic Authentication for this service (see below).
* `"auth": "none"`: Explicitly disables authentication for this service.
2. **Global Exclusions:** A comma-separated list of service names to exclude from the global authenticator.
* `PROXY_AUTH_EXCLUDE=ntfy,public-dashboard`
3. **Global Default (Lowest Priority):** A single variable sets the default authentication provider for all services that don't have a per-service override and are not in the exclusion list.
* `PROXY_AUTH_PROVIDER=authelia` (can be `ldap`, `authentik`, etc.)
**Basic Authentication**
If you set `"auth": "basic"` for any service, you must also provide the credentials using these two environment variables. The container will automatically create the necessary `.htpasswd` file.
* `PROXY_AUTH_BASIC_USER`: The username for basic authentication.
* `PROXY_AUTH_BASIC_PASS`: The password for basic authentication.
Example:
```yaml
environment:
- 'PROXY_CONFIG_PORTAINER={"port": 9000, "auth": "basic"}'
- PROXY_AUTH_BASIC_USER=myadmin
- PROXY_AUTH_BASIC_PASS=supersecretpassword
```
### Security and password protection ### Security and password protection
* The container detects changes to url and subdomains, revokes existing certs and generates new ones during start. * The container detects changes to url and subdomains, revokes existing certs and generates new ones during start.
@@ -170,7 +252,7 @@ This image can be run with a read-only container filesystem. For details please
To help you get started creating a container from this image you can either use docker-compose or the docker cli. To help you get started creating a container from this image you can either use docker-compose or the docker cli.
>[!NOTE] >[!NOTE]
>Unless a parameter is flagged as 'optional', it is *mandatory* and a value must be provided. >Unless a parameter is flaged as 'optional', it is *mandatory* and a value must be provided.
### docker-compose (recommended, [click here for more info](https://docs.linuxserver.io/general/docker-compose)) ### docker-compose (recommended, [click here for more info](https://docs.linuxserver.io/general/docker-compose))
@@ -254,7 +336,7 @@ Containers are configured using parameters passed at runtime (such as those abov
| `-e VALIDATION=http` | Certbot validation method to use, options are `http` or `dns` (`dns` method also requires `DNSPLUGIN` variable set). | | `-e VALIDATION=http` | Certbot validation method to use, options are `http` or `dns` (`dns` method also requires `DNSPLUGIN` variable set). |
| `-e SUBDOMAINS=www,` | Subdomains you'd like the cert to cover (comma separated, no spaces) ie. `www,ftp,cloud`. For a wildcard cert, set this *exactly* to `wildcard` (wildcard cert is available via `dns` validation only) | | `-e SUBDOMAINS=www,` | Subdomains you'd like the cert to cover (comma separated, no spaces) ie. `www,ftp,cloud`. For a wildcard cert, set this *exactly* to `wildcard` (wildcard cert is available via `dns` validation only) |
| `-e CERTPROVIDER=` | Optionally define the cert provider. Set to `zerossl` for ZeroSSL certs (requires existing [ZeroSSL account](https://app.zerossl.com/signup) and the e-mail address entered in `EMAIL` env var). Otherwise defaults to Let's Encrypt. | | `-e CERTPROVIDER=` | Optionally define the cert provider. Set to `zerossl` for ZeroSSL certs (requires existing [ZeroSSL account](https://app.zerossl.com/signup) and the e-mail address entered in `EMAIL` env var). Otherwise defaults to Let's Encrypt. |
| `-e DNSPLUGIN=cloudflare` | Required if `VALIDATION` is set to `dns`. Options are `acmedns`, `aliyun`, `azure`, `bunny`, `cloudflare`, `cpanel`, `desec`, `digitalocean`, `directadmin`, `dnsimple`, `dnsmadeeasy`, `dnspod`, `do`, `domeneshop`, `dreamhost`, `duckdns`, `dynu`, `freedns`, `gandi`, `gehirn`, `glesys`, `godaddy`, `google`, `he`, `hetzner`, `hetzner-cloud`, `infomaniak`, `inwx`, `ionos`, `linode`, `loopia`, `luadns`, `namecheap`, `netcup`, `njalla`, `nsone`, `ovh`, `porkbun`, `rfc2136`, `route53`, `sakuracloud`, `standalone`, `transip`, and `vultr`. Also need to enter the credentials into the corresponding ini (or json for some plugins) file under `/config/dns-conf`. | | `-e DNSPLUGIN=cloudflare` | Required if `VALIDATION` is set to `dns`. Options are `acmedns`, `aliyun`, `azure`, `bunny`, `cloudflare`, `cpanel`, `desec`, `digitalocean`, `directadmin`, `dnsimple`, `dnsmadeeasy`, `dnspod`, `do`, `domeneshop`, `dreamhost`, `duckdns`, `dynu`, `freedns`, `gandi`, `gehirn`, `glesys`, `godaddy`, `google`, `he`, `hetzner`, `infomaniak`, `inwx`, `ionos`, `linode`, `loopia`, `luadns`, `namecheap`, `netcup`, `njalla`, `nsone`, `ovh`, `porkbun`, `rfc2136`, `route53`, `sakuracloud`, `standalone`, `transip`, and `vultr`. Also need to enter the credentials into the corresponding ini (or json for some plugins) file under `/config/dns-conf`. |
| `-e PROPAGATION=` | Optionally override (in seconds) the default propagation time for the dns plugins. | | `-e PROPAGATION=` | Optionally override (in seconds) the default propagation time for the dns plugins. |
| `-e EMAIL=` | Optional e-mail address used for cert expiration notifications (Required for ZeroSSL). | | `-e EMAIL=` | Optional e-mail address used for cert expiration notifications (Required for ZeroSSL). |
| `-e ONLY_SUBDOMAINS=false` | If you wish to get certs only for certain subdomains, but not the main domain (main domain may be hosted on another machine and cannot be validated), set this to `true` | | `-e ONLY_SUBDOMAINS=false` | If you wish to get certs only for certain subdomains, but not the main domain (main domain may be hosted on another machine and cannot be validated), set this to `true` |
@@ -433,9 +515,7 @@ Once registered you can define the dockerfile to use with `-f Dockerfile.aarch64
## Versions ## Versions
* **23.01.26:** - Reorder init to fix proxy conf version checks. * **02.09.25:** - Add ability to define proxy configurations via environment variables.
* **21.12.25:** - Add support for hetzner-cloud dns validation.
* **04.11.25:** - Switch default Gandi credentials from API Key to Token, allow DNS propagation time for Azure DNS plugin.
* **18.07.25:** - Rebase to Alpine 3.22 with PHP 8.4. Add QUIC support. Drop PHP bindings for mcrypt as it is no longer maintained. * **18.07.25:** - Rebase to Alpine 3.22 with PHP 8.4. Add QUIC support. Drop PHP bindings for mcrypt as it is no longer maintained.
* **05.05.25:** - Disable Certbot's built in log rotation. * **05.05.25:** - Disable Certbot's built in log rotation.
* **19.01.25:** - Add [Auto Reload](https://github.com/linuxserver/docker-mods/tree/swag-auto-reload) functionality to SWAG. * **19.01.25:** - Add [Auto Reload](https://github.com/linuxserver/docker-mods/tree/swag-auto-reload) functionality to SWAG.

View File

@@ -1,110 +1,109 @@
NAME VERSION TYPE NAME VERSION TYPE
Simple Launcher 1.1.0.14 binary (+5 duplicates) Simple Launcher 1.1.0.14 binary (+5 duplicates)
acl-libs 2.3.2-r1 apk acl-libs 2.3.2-r1 apk
acme 5.3.1 python acme 4.2.0 python
alpine-baselayout 3.7.0-r0 apk alpine-baselayout 3.7.0-r0 apk
alpine-baselayout-data 3.7.0-r0 apk alpine-baselayout-data 3.7.0-r0 apk
alpine-keys 2.5-r0 apk alpine-keys 2.5-r0 apk
alpine-release 3.22.3-r0 apk alpine-release 3.22.1-r0 apk
aom-libs 3.12.1-r0 apk aom-libs 3.12.1-r0 apk
apache2-utils 2.4.66-r0 apk apache2-utils 2.4.65-r0 apk
apk-tools 2.14.9-r3 apk apk-tools 2.14.9-r2 apk
apr 1.7.5-r0 apk apr 1.7.5-r0 apk
apr-util 1.6.3-r1 apk apr-util 1.6.3-r1 apk
argon2-libs 20190702-r5 apk argon2-libs 20190702-r5 apk
attrs 25.4.0 python attrs 25.3.0 python
autocommand 2.2.2 python autocommand 2.2.2 python
azure-common 1.1.28 python azure-common 1.1.28 python
azure-core 1.38.2 python azure-core 1.35.0 python
azure-identity 1.25.2 python azure-identity 1.24.0 python
azure-mgmt-core 1.6.0 python azure-mgmt-core 1.6.0 python
azure-mgmt-dns 9.0.0 python azure-mgmt-dns 9.0.0 python
backports-tarfile 1.2.0 python backports-tarfile 1.2.0 python
bash 5.2.37-r0 apk bash 5.2.37-r0 apk
beautifulsoup4 4.14.3 python beautifulsoup4 4.13.5 python
boto3 1.42.63 python boto3 1.40.21 python
botocore 1.42.63 python botocore 1.40.21 python
brotli-libs 1.1.0-r2 apk brotli-libs 1.1.0-r2 apk
bs4 0.0.2 python bs4 0.0.2 python
busybox 1.37.0-r20 apk busybox 1.37.0-r19 apk
busybox-binsh 1.37.0-r20 apk busybox-binsh 1.37.0-r19 apk
c-ares 1.34.6-r0 apk c-ares 1.34.5-r0 apk
c-client 2007f-r15 apk c-client 2007f-r15 apk
ca-certificates 20250911-r0 apk ca-certificates 20250619-r0 apk
ca-certificates-bundle 20250911-r0 apk ca-certificates-bundle 20250619-r0 apk
cachetools 5.5.2 python
catatonit 0.2.1-r0 apk catatonit 0.2.1-r0 apk
certbot 5.3.1 python certbot 4.2.0 python
certbot-dns-acmedns 0.1.0 python certbot-dns-acmedns 0.1.0 python
certbot-dns-aliyun 2.0.0 python certbot-dns-aliyun 2.0.0 python
certbot-dns-azure 1.5.0 python certbot-dns-azure 1.5.0 python
certbot-dns-bunny 3.0.0 python certbot-dns-bunny 3.0.0 python
certbot-dns-cloudflare 5.3.1 python certbot-dns-cloudflare 4.2.0 python
certbot-dns-cpanel 0.4.0 python certbot-dns-cpanel 0.4.0 python
certbot-dns-desec 1.3.2 python certbot-dns-desec 1.2.1 python
certbot-dns-digitalocean 5.3.1 python certbot-dns-digitalocean 4.2.0 python
certbot-dns-directadmin 1.0.15 python certbot-dns-directadmin 1.0.15 python
certbot-dns-dnsimple 5.3.1 python certbot-dns-dnsimple 4.2.0 python
certbot-dns-dnsmadeeasy 5.3.1 python certbot-dns-dnsmadeeasy 4.2.0 python
certbot-dns-dnspod 0.1.0 python certbot-dns-dnspod 0.1.0 python
certbot-dns-do 0.31.0 python certbot-dns-do 0.31.0 python
certbot-dns-domeneshop 0.2.9 python certbot-dns-domeneshop 0.2.9 python
certbot-dns-dreamhost 1.0 python certbot-dns-dreamhost 1.0 python
certbot-dns-duckdns 1.8.0 python certbot-dns-duckdns 1.6 python
certbot-dns-dynudns 0.0.6 python certbot-dns-dynudns 0.0.6 python
certbot-dns-freedns 0.2.0 python certbot-dns-freedns 0.2.0 python
certbot-dns-gehirn 5.3.1 python certbot-dns-gehirn 4.2.0 python
certbot-dns-glesys 2.1.0 python certbot-dns-glesys 2.1.0 python
certbot-dns-godaddy 2.8.0 python certbot-dns-godaddy 2.8.0 python
certbot-dns-google 5.3.1 python certbot-dns-google 4.2.0 python
certbot-dns-he 1.0.0 python certbot-dns-he 1.0.0 python
certbot-dns-hetzner 3.0.0 python certbot-dns-hetzner 2.0.1 python
certbot-dns-hetzner-cloud 1.0.5 python certbot-dns-infomaniak 0.2.3 python
certbot-dns-infomaniak 0.2.4 python
certbot-dns-inwx 3.0.3 python certbot-dns-inwx 3.0.3 python
certbot-dns-ionos 2024.11.9 python certbot-dns-ionos 2024.11.9 python
certbot-dns-linode 5.3.1 python certbot-dns-linode 4.2.0 python
certbot-dns-loopia 1.0.1 python certbot-dns-loopia 1.0.1 python
certbot-dns-luadns 5.3.1 python certbot-dns-luadns 4.2.0 python
certbot-dns-namecheap 1.0.0 python certbot-dns-namecheap 1.0.0 python
certbot-dns-netcup 2.0.0 python certbot-dns-netcup 1.4.4 python
certbot-dns-njalla 2.0.2 python certbot-dns-njalla 2.0.2 python
certbot-dns-nsone 5.3.1 python certbot-dns-nsone 4.2.0 python
certbot-dns-ovh 5.3.1 python certbot-dns-ovh 4.2.0 python
certbot-dns-porkbun 0.11.0 python certbot-dns-porkbun 0.10.1 python
certbot-dns-rfc2136 5.3.1 python certbot-dns-rfc2136 4.2.0 python
certbot-dns-route53 5.3.1 python certbot-dns-route53 4.2.0 python
certbot-dns-sakuracloud 5.3.1 python certbot-dns-sakuracloud 4.2.0 python
certbot-dns-standalone 1.2.1 python certbot-dns-standalone 1.2.1 python
certbot-dns-transip 0.5.2 python certbot-dns-transip 0.5.2 python
certbot-dns-vultr 1.1.0 python certbot-dns-vultr 1.1.0 python
certbot-plugin-gandi 1.5.0 python certbot-plugin-gandi 1.5.0 python
certifi 2026.2.25 python certifi 2025.8.3 python
cffi 2.0.0 python cffi 1.17.1 python
charset-normalizer 3.4.5 python charset-normalizer 3.4.3 python
cli UNKNOWN binary cli UNKNOWN binary
cli-32 UNKNOWN binary cli-32 UNKNOWN binary
cli-64 UNKNOWN binary cli-64 UNKNOWN binary
cli-arm64 UNKNOWN binary cli-arm64 UNKNOWN binary
cloudflare 2.19.4 python cloudflare 2.19.4 python
composer 2.9.5 binary composer 2.8.11 binary
configargparse 1.7.1 python configargparse 1.7.1 python
configobj 5.0.9 python configobj 5.0.9 python
coreutils 9.7-r1 apk coreutils 9.7-r1 apk
coreutils-env 9.7-r1 apk coreutils-env 9.7-r1 apk
coreutils-fmt 9.7-r1 apk coreutils-fmt 9.7-r1 apk
coreutils-sha512sum 9.7-r1 apk coreutils-sha512sum 9.7-r1 apk
cryptography 46.0.5 python cryptography 45.0.6 python
curl 8.14.1-r2 apk curl 8.14.1-r1 apk
distro 1.9.0 python distro 1.9.0 python
dns-lexicon 3.23.2 python dns-lexicon 3.21.1 python
dns-lexicon-coop 3.24.2 python
dnslib 0.9.26 python dnslib 0.9.26 python
dnspython 2.8.0 python dnspython 2.7.0 python
domeneshop 0.4.4 python domeneshop 0.4.4 python
fail2ban 1.1.0 python fail2ban 1.1.0 python
fail2ban 1.1.0-r3 apk fail2ban 1.1.0-r3 apk
fail2ban-pyc 1.1.0-r3 apk fail2ban-pyc 1.1.0-r3 apk
filelock 3.25.0 python filelock 3.19.1 python
findutils 4.10.0-r0 apk findutils 4.10.0-r0 apk
fontconfig 2.15.0-r3 apk fontconfig 2.15.0-r3 apk
freetype 2.13.3-r0 apk freetype 2.13.3-r0 apk
@@ -114,60 +113,61 @@ git 2.49.1-r0 apk
git-init-template 2.49.1-r0 apk git-init-template 2.49.1-r0 apk
git-perl 2.49.1-r0 apk git-perl 2.49.1-r0 apk
gmp 6.3.0-r3 apk gmp 6.3.0-r3 apk
gnupg 2.4.9-r0 apk gnupg 2.4.7-r0 apk
gnupg-dirmngr 2.4.9-r0 apk gnupg-dirmngr 2.4.7-r0 apk
gnupg-gpgconf 2.4.9-r0 apk gnupg-gpgconf 2.4.7-r0 apk
gnupg-keyboxd 2.4.9-r0 apk gnupg-keyboxd 2.4.7-r0 apk
gnupg-utils 2.4.9-r0 apk gnupg-utils 2.4.7-r0 apk
gnupg-wks-client 2.4.9-r0 apk gnupg-wks-client 2.4.7-r0 apk
gnutls 3.8.12-r0 apk gnutls 3.8.8-r0 apk
google-api-core 2.30.0 python google-api-core 2.25.1 python
google-api-python-client 2.192.0 python google-api-python-client 2.179.0 python
google-auth 2.49.0 python google-auth 2.40.3 python
google-auth-httplib2 0.3.0 python google-auth-httplib2 0.2.0 python
googleapis-common-protos 1.73.0 python googleapis-common-protos 1.70.0 python
gpg 2.4.9-r0 apk gpg 2.4.7-r0 apk
gpg-agent 2.4.9-r0 apk gpg-agent 2.4.7-r0 apk
gpg-wks-server 2.4.9-r0 apk gpg-wks-server 2.4.7-r0 apk
gpgsm 2.4.9-r0 apk gpgsm 2.4.7-r0 apk
gpgv 2.4.9-r0 apk gpgv 2.4.7-r0 apk
gui UNKNOWN binary gui UNKNOWN binary
gui-32 UNKNOWN binary gui-32 UNKNOWN binary
gui-64 UNKNOWN binary gui-64 UNKNOWN binary
gui-arm64 UNKNOWN binary gui-arm64 UNKNOWN binary
hcloud 2.17.0 python httplib2 0.30.0 python
httplib2 0.31.2 python
icu-data-en 76.1-r1 apk icu-data-en 76.1-r1 apk
icu-libs 76.1-r1 apk icu-libs 76.1-r1 apk
idna 3.11 python idna 3.10 python
importlib-metadata 8.7.1 python importlib-metadata 8.0.0 python
inflect 7.3.1 python
inotify-tools 4.23.9.0-r0 apk inotify-tools 4.23.9.0-r0 apk
inotify-tools-libs 4.23.9.0-r0 apk inotify-tools-libs 4.23.9.0-r0 apk
inwx-domrobot 3.2.0 python inwx-domrobot 3.2.0 python
iptables 1.8.11-r1 apk iptables 1.8.11-r1 apk
iptables-legacy 1.8.11-r1 apk iptables-legacy 1.8.11-r1 apk
isodate 0.7.2 python isodate 0.7.2 python
jaraco-context 6.1.0 python jaraco-collections 5.1.0 python
jaraco-functools 4.4.0 python jaraco-context 5.3.0 python
jaraco-text 4.0.0 python jaraco-functools 4.0.1 python
jaraco-text 3.12.1 python
jinja2 3.1.6 python jinja2 3.1.6 python
jmespath 1.1.0 python jmespath 1.0.1 python
josepy 2.2.0 python josepy 2.1.0 python
jq 1.8.1-r0 apk jq 1.8.0-r0 apk
jsonlines 4.0.0 python jsonlines 4.0.0 python
jsonpickle 4.1.1 python jsonpickle 4.1.1 python
libapk2 2.14.9-r3 apk libapk2 2.14.9-r2 apk
libassuan 2.5.7-r0 apk libassuan 2.5.7-r0 apk
libattr 2.5.2-r2 apk libattr 2.5.2-r2 apk
libavif 1.3.0-r0 apk libavif 1.3.0-r0 apk
libbsd 0.12.2-r0 apk libbsd 0.12.2-r0 apk
libbz2 1.0.8-r6 apk libbz2 1.0.8-r6 apk
libcrypto3 3.5.5-r0 apk libcrypto3 3.5.2-r0 apk
libcurl 8.14.1-r2 apk libcurl 8.14.1-r1 apk
libdav1d 1.5.1-r0 apk libdav1d 1.5.1-r0 apk
libedit 20250104.3.1-r1 apk libedit 20250104.3.1-r1 apk
libevent 2.1.12-r8 apk libevent 2.1.12-r8 apk
libexpat 2.7.4-r0 apk libexpat 2.7.1-r0 apk
libffi 3.4.8-r0 apk libffi 3.4.8-r0 apk
libgcc 14.2.0-r6 apk libgcc 14.2.0-r6 apk
libgcrypt 1.10.3-r1 apk libgcrypt 1.10.3-r1 apk
@@ -188,18 +188,18 @@ libmnl 1.0.5-r2 apk
libncursesw 6.5_p20250503-r0 apk libncursesw 6.5_p20250503-r0 apk
libnftnl 1.2.9-r0 apk libnftnl 1.2.9-r0 apk
libpanelw 6.5_p20250503-r0 apk libpanelw 6.5_p20250503-r0 apk
libpng 1.6.55-r0 apk libpng 1.6.47-r0 apk
libpq 17.8-r0 apk libpq 17.6-r0 apk
libproc2 4.0.4-r3 apk libproc2 4.0.4-r3 apk
libpsl 0.21.5-r3 apk libpsl 0.21.5-r3 apk
libsasl 2.1.28-r8 apk libsasl 2.1.28-r8 apk
libseccomp 2.6.0-r0 apk libseccomp 2.6.0-r0 apk
libsharpyuv 1.5.0-r0 apk libsharpyuv 1.5.0-r0 apk
libsm 1.2.5-r0 apk libsm 1.2.5-r0 apk
libsodium 1.0.20-r1 apk libsodium 1.0.20-r0 apk
libssl3 3.5.5-r0 apk libssl3 3.5.2-r0 apk
libstdc++ 14.2.0-r6 apk libstdc++ 14.2.0-r6 apk
libtasn1 4.21.0-r0 apk libtasn1 4.20.0-r0 apk
libunistring 1.3-r0 apk libunistring 1.3-r0 apk
libuuid 2.41-r9 apk libuuid 2.41-r9 apk
libwebp 1.5.0-r0 apk libwebp 1.5.0-r0 apk
@@ -208,7 +208,7 @@ libxau 1.0.12-r0 apk
libxcb 1.17.0-r0 apk libxcb 1.17.0-r0 apk
libxdmcp 1.1.5-r1 apk libxdmcp 1.1.5-r1 apk
libxext 1.3.6-r2 apk libxext 1.3.6-r2 apk
libxml2 2.13.9-r0 apk libxml2 2.13.8-r0 apk
libxpm 3.5.17-r0 apk libxpm 3.5.17-r0 apk
libxslt 1.1.43-r3 apk libxslt 1.1.43-r3 apk
libxt 1.3.1-r0 apk libxt 1.3.1-r0 apk
@@ -218,152 +218,155 @@ libzip 1.11.4-r0 apk
linux-pam 1.7.0-r4 apk linux-pam 1.7.0-r4 apk
logrotate 3.21.0-r1 apk logrotate 3.21.0-r1 apk
loopialib 0.2.0 python loopialib 0.2.0 python
lxml 6.0.2 python lxml 6.0.1 python
lz4-libs 1.10.0-r0 apk lz4-libs 1.10.0-r0 apk
markupsafe 3.0.3 python markupsafe 3.0.2 python
memcached 1.6.32-r0 apk memcached 1.6.32-r0 apk
mock 5.2.0 python mock 5.2.0 python
more-itertools 10.8.0 python more-itertools 10.3.0 python
mpdecimal 4.0.1-r0 apk mpdecimal 4.0.1-r0 apk
msal 1.35.1 python msal 1.33.0 python
msal-extensions 1.3.1 python msal-extensions 1.3.1 python
musl 1.2.5-r10 apk musl 1.2.5-r10 apk
musl-utils 1.2.5-r10 apk musl-utils 1.2.5-r10 apk
my-test-package 1.0 python
nano 8.4-r0 apk nano 8.4-r0 apk
ncurses-terminfo-base 6.5_p20250503-r0 apk ncurses-terminfo-base 6.5_p20250503-r0 apk
netcat-openbsd 1.229.1-r0 apk netcat-openbsd 1.229.1-r0 apk
nettle 3.10.2-r0 apk nettle 3.10.1-r0 apk
nghttp2-libs 1.65.0-r0 apk nghttp2-libs 1.65.0-r0 apk
nginx 1.28.2-r0 apk nginx 1.28.0-r3 apk
nginx-mod-devel-kit 1.28.2-r0 apk nginx-mod-devel-kit 1.28.0-r3 apk
nginx-mod-http-brotli 1.28.2-r0 apk nginx-mod-http-brotli 1.28.0-r3 apk
nginx-mod-http-dav-ext 1.28.2-r0 apk nginx-mod-http-dav-ext 1.28.0-r3 apk
nginx-mod-http-echo 1.28.2-r0 apk nginx-mod-http-echo 1.28.0-r3 apk
nginx-mod-http-fancyindex 1.28.2-r0 apk nginx-mod-http-fancyindex 1.28.0-r3 apk
nginx-mod-http-geoip2 1.28.2-r0 apk nginx-mod-http-geoip2 1.28.0-r3 apk
nginx-mod-http-headers-more 1.28.2-r0 apk nginx-mod-http-headers-more 1.28.0-r3 apk
nginx-mod-http-image-filter 1.28.2-r0 apk nginx-mod-http-image-filter 1.28.0-r3 apk
nginx-mod-http-perl 1.28.2-r0 apk nginx-mod-http-perl 1.28.0-r3 apk
nginx-mod-http-redis2 1.28.2-r0 apk nginx-mod-http-redis2 1.28.0-r3 apk
nginx-mod-http-set-misc 1.28.2-r0 apk nginx-mod-http-set-misc 1.28.0-r3 apk
nginx-mod-http-upload-progress 1.28.2-r0 apk nginx-mod-http-upload-progress 1.28.0-r3 apk
nginx-mod-http-xslt-filter 1.28.2-r0 apk nginx-mod-http-xslt-filter 1.28.0-r3 apk
nginx-mod-mail 1.28.2-r0 apk nginx-mod-mail 1.28.0-r3 apk
nginx-mod-rtmp 1.28.2-r0 apk nginx-mod-rtmp 1.28.0-r3 apk
nginx-mod-stream 1.28.2-r0 apk nginx-mod-stream 1.28.0-r3 apk
nginx-mod-stream-geoip2 1.28.2-r0 apk nginx-mod-stream-geoip2 1.28.0-r3 apk
nginx-vim 1.28.2-r0 apk nginx-vim 1.28.0-r3 apk
npth 1.8-r0 apk npth 1.8-r0 apk
oniguruma 6.9.10-r0 apk oniguruma 6.9.10-r0 apk
openssl 3.5.5-r0 apk openssl 3.5.2-r0 apk
p11-kit 0.25.5-r2 apk p11-kit 0.25.5-r2 apk
packaging 26.0 python (+1 duplicate) packaging 24.2 python
parsedatetime 2.6 python parsedatetime 2.6 python
pcre2 10.46-r0 apk pcre2 10.43-r1 apk
perl 5.40.3-r0 apk perl 5.40.3-r0 apk
perl-error 0.17030-r0 apk perl-error 0.17030-r0 apk
perl-git 2.49.1-r0 apk perl-git 2.49.1-r0 apk
php84 8.4.16-r0 apk php84 8.4.11-r0 apk
php84-bcmath 8.4.16-r0 apk php84-bcmath 8.4.11-r0 apk
php84-bz2 8.4.16-r0 apk php84-bz2 8.4.11-r0 apk
php84-common 8.4.16-r0 apk php84-common 8.4.11-r0 apk
php84-ctype 8.4.16-r0 apk php84-ctype 8.4.11-r0 apk
php84-curl 8.4.16-r0 apk php84-curl 8.4.11-r0 apk
php84-dom 8.4.16-r0 apk php84-dom 8.4.11-r0 apk
php84-exif 8.4.16-r0 apk php84-exif 8.4.11-r0 apk
php84-fileinfo 8.4.16-r0 apk php84-fileinfo 8.4.11-r0 apk
php84-fpm 8.4.16-r0 apk php84-fpm 8.4.11-r0 apk
php84-ftp 8.4.16-r0 apk php84-ftp 8.4.11-r0 apk
php84-gd 8.4.16-r0 apk php84-gd 8.4.11-r0 apk
php84-gmp 8.4.16-r0 apk php84-gmp 8.4.11-r0 apk
php84-iconv 8.4.16-r0 apk php84-iconv 8.4.11-r0 apk
php84-intl 8.4.16-r0 apk php84-intl 8.4.11-r0 apk
php84-ldap 8.4.16-r0 apk php84-ldap 8.4.11-r0 apk
php84-mbstring 8.4.16-r0 apk php84-mbstring 8.4.11-r0 apk
php84-mysqli 8.4.16-r0 apk php84-mysqli 8.4.11-r0 apk
php84-mysqlnd 8.4.16-r0 apk php84-mysqlnd 8.4.11-r0 apk
php84-opcache 8.4.16-r0 apk php84-opcache 8.4.11-r0 apk
php84-openssl 8.4.16-r0 apk php84-openssl 8.4.11-r0 apk
php84-pdo 8.4.16-r0 apk php84-pdo 8.4.11-r0 apk
php84-pdo_mysql 8.4.16-r0 apk php84-pdo_mysql 8.4.11-r0 apk
php84-pdo_odbc 8.4.16-r0 apk php84-pdo_odbc 8.4.11-r0 apk
php84-pdo_pgsql 8.4.16-r0 apk php84-pdo_pgsql 8.4.11-r0 apk
php84-pdo_sqlite 8.4.16-r0 apk php84-pdo_sqlite 8.4.11-r0 apk
php84-pear 8.4.16-r0 apk php84-pear 8.4.11-r0 apk
php84-pecl-apcu 5.1.27-r0 apk php84-pecl-apcu 5.1.27-r0 apk
php84-pecl-igbinary 3.2.16-r1 apk php84-pecl-igbinary 3.2.16-r1 apk
php84-pecl-imap 1.0.3-r0 apk php84-pecl-imap 1.0.3-r0 apk
php84-pecl-memcached 3.3.0-r0 apk php84-pecl-memcached 3.3.0-r0 apk
php84-pecl-msgpack 3.0.0-r0 apk php84-pecl-msgpack 3.0.0-r0 apk
php84-pecl-redis 6.3.0-r0 apk php84-pecl-redis 6.2.0-r0 apk
php84-pgsql 8.4.16-r0 apk php84-pgsql 8.4.11-r0 apk
php84-phar 8.4.16-r0 apk php84-phar 8.4.11-r0 apk
php84-posix 8.4.16-r0 apk php84-posix 8.4.11-r0 apk
php84-session 8.4.16-r0 apk php84-session 8.4.11-r0 apk
php84-simplexml 8.4.16-r0 apk php84-simplexml 8.4.11-r0 apk
php84-soap 8.4.16-r0 apk php84-soap 8.4.11-r0 apk
php84-sockets 8.4.16-r0 apk php84-sockets 8.4.11-r0 apk
php84-sodium 8.4.16-r0 apk php84-sodium 8.4.11-r0 apk
php84-sqlite3 8.4.16-r0 apk php84-sqlite3 8.4.11-r0 apk
php84-tokenizer 8.4.16-r0 apk php84-tokenizer 8.4.11-r0 apk
php84-xml 8.4.16-r0 apk php84-xml 8.4.11-r0 apk
php84-xmlreader 8.4.16-r0 apk php84-xmlreader 8.4.11-r0 apk
php84-xmlwriter 8.4.16-r0 apk php84-xmlwriter 8.4.11-r0 apk
php84-xsl 8.4.16-r0 apk php84-xsl 8.4.11-r0 apk
php84-zip 8.4.16-r0 apk php84-zip 8.4.11-r0 apk
pinentry 1.3.1-r0 apk pinentry 1.3.1-r0 apk
pip 26.0.1 python pip 25.2 python
pkb-client 2.2.0 python pkb-client 2.2.0 python
platformdirs 4.4.0 python platformdirs 4.2.2 python
popt 1.19-r4 apk popt 1.19-r4 apk
procps-ng 4.0.4-r3 apk procps-ng 4.0.4-r3 apk
proto-plus 1.27.1 python proto-plus 1.26.1 python
protobuf 6.33.5 python protobuf 6.32.0 python
pyacmedns 0.4 python pyacmedns 0.4 python
pyasn1 0.6.2 python pyasn1 0.6.1 python
pyasn1-modules 0.4.2 python pyasn1-modules 0.4.2 python
pyc 3.12.12-r0 apk pyc 3.12.11-r0 apk
pycparser 3.0 python pycparser 2.22 python
pyjwt 2.11.0 python pyjwt 2.10.1 python
pynamecheap 0.0.3 python pynamecheap 0.0.3 python
pyopenssl 25.3.0 python pyopenssl 25.1.0 python
pyotp 2.9.0 python pyotp 2.9.0 python
pyparsing 3.3.2 python pyparsing 3.2.3 python
pyrfc3339 2.1.0 python pyrfc3339 2.1.0 python
python-dateutil 2.9.0.post0 python python-dateutil 2.9.0.post0 python
python-digitalocean 1.17.0 python python-digitalocean 1.17.0 python
python-transip 0.6.0 python python-transip 0.6.0 python
python3 3.12.12-r0 apk python3 3.12.11-r0 apk
python3-pyc 3.12.12-r0 apk python3-pyc 3.12.11-r0 apk
python3-pycache-pyc0 3.12.12-r0 apk python3-pycache-pyc0 3.12.11-r0 apk
pyyaml 6.0.3 python pyyaml 6.0.2 python
readline 8.2.13-r1 apk readline 8.2.13-r1 apk
requests 2.32.5 python requests 2.32.5 python
requests-file 3.0.1 python requests-file 2.1.0 python
requests-mock 1.12.1 python requests-mock 1.12.1 python
rsa 4.9.1 python rsa 4.9.1 python
s3transfer 0.16.0 python s3transfer 0.13.1 python
scanelf 1.3.8-r1 apk scanelf 1.3.8-r1 apk
setuptools 82.0.0 python setuptools 80.9.0 python
shadow 4.17.3-r0 apk shadow 4.17.3-r0 apk
six 1.17.0 python six 1.17.0 python
skalibs-libs 2.14.4.0-r0 apk skalibs-libs 2.14.4.0-r0 apk
soupsieve 2.8.3 python soupsieve 2.8 python
sqlite-libs 3.49.2-r1 apk sqlite-libs 3.49.2-r1 apk
ssl_client 1.37.0-r20 apk ssl_client 1.37.0-r19 apk
tiff 4.7.1-r0 apk tiff 4.7.0-r0 apk
tldextract 5.3.1 python tldextract 5.3.0 python
tomli 2.4.0 python tomli 2.0.1 python
typeguard 4.3.0 python
typing-extensions 4.12.2 python
typing-extensions 4.15.0 python typing-extensions 4.15.0 python
tzdata 2025c-r0 apk tzdata 2025b-r0 apk
unixodbc 2.3.12-r0 apk unixodbc 2.3.12-r0 apk
uritemplate 4.2.0 python uritemplate 4.2.0 python
urllib3 2.6.3 python urllib3 2.5.0 python
utmps-libs 0.1.3.1-r0 apk utmps-libs 0.1.3.1-r0 apk
wheel 0.46.3 python (+1 duplicate) wheel 0.45.1 python (+1 duplicate)
whois 5.6.3-r0 apk whois 5.6.3-r0 apk
xz-libs 5.8.1-r0 apk xz-libs 5.8.1-r0 apk
zipp 3.23.0 python zipp 3.19.2 python
zlib 1.3.1-r2 apk zlib 1.3.1-r2 apk
zope-interface 8.2 python zope-interface 7.2 python
zstd-libs 1.5.7-r0 apk zstd-libs 1.5.7-r0 apk

View File

@@ -32,7 +32,7 @@ opt_param_usage_include_env: true
opt_param_env_vars: opt_param_env_vars:
- {env_var: "SUBDOMAINS", env_value: "www,", desc: "Subdomains you'd like the cert to cover (comma separated, no spaces) ie. `www,ftp,cloud`. For a wildcard cert, set this *exactly* to `wildcard` (wildcard cert is available via `dns` validation only)"} - {env_var: "SUBDOMAINS", env_value: "www,", desc: "Subdomains you'd like the cert to cover (comma separated, no spaces) ie. `www,ftp,cloud`. For a wildcard cert, set this *exactly* to `wildcard` (wildcard cert is available via `dns` validation only)"}
- {env_var: "CERTPROVIDER", env_value: "", desc: "Optionally define the cert provider. Set to `zerossl` for ZeroSSL certs (requires existing [ZeroSSL account](https://app.zerossl.com/signup) and the e-mail address entered in `EMAIL` env var). Otherwise defaults to Let's Encrypt."} - {env_var: "CERTPROVIDER", env_value: "", desc: "Optionally define the cert provider. Set to `zerossl` for ZeroSSL certs (requires existing [ZeroSSL account](https://app.zerossl.com/signup) and the e-mail address entered in `EMAIL` env var). Otherwise defaults to Let's Encrypt."}
- {env_var: "DNSPLUGIN", env_value: "cloudflare", desc: "Required if `VALIDATION` is set to `dns`. Options are `acmedns`, `aliyun`, `azure`, `bunny`, `cloudflare`, `cpanel`, `desec`, `digitalocean`, `directadmin`, `dnsimple`, `dnsmadeeasy`, `dnspod`, `do`, `domeneshop`, `dreamhost`, `duckdns`, `dynu`, `freedns`, `gandi`, `gehirn`, `glesys`, `godaddy`, `google`, `he`, `hetzner`, `hetzner-cloud`, `infomaniak`, `inwx`, `ionos`, `linode`, `loopia`, `luadns`, `namecheap`, `netcup`, `njalla`, `nsone`, `ovh`, `porkbun`, `rfc2136`, `route53`, `sakuracloud`, `standalone`, `transip`, and `vultr`. Also need to enter the credentials into the corresponding ini (or json for some plugins) file under `/config/dns-conf`."} - {env_var: "DNSPLUGIN", env_value: "cloudflare", desc: "Required if `VALIDATION` is set to `dns`. Options are `acmedns`, `aliyun`, `azure`, `bunny`, `cloudflare`, `cpanel`, `desec`, `digitalocean`, `directadmin`, `dnsimple`, `dnsmadeeasy`, `dnspod`, `do`, `domeneshop`, `dreamhost`, `duckdns`, `dynu`, `freedns`, `gandi`, `gehirn`, `glesys`, `godaddy`, `google`, `he`, `hetzner`, `infomaniak`, `inwx`, `ionos`, `linode`, `loopia`, `luadns`, `namecheap`, `netcup`, `njalla`, `nsone`, `ovh`, `porkbun`, `rfc2136`, `route53`, `sakuracloud`, `standalone`, `transip`, and `vultr`. Also need to enter the credentials into the corresponding ini (or json for some plugins) file under `/config/dns-conf`."}
- {env_var: "PROPAGATION", env_value: "", desc: "Optionally override (in seconds) the default propagation time for the dns plugins."} - {env_var: "PROPAGATION", env_value: "", desc: "Optionally override (in seconds) the default propagation time for the dns plugins."}
- {env_var: "EMAIL", env_value: "", desc: "Optional e-mail address used for cert expiration notifications (Required for ZeroSSL)."} - {env_var: "EMAIL", env_value: "", desc: "Optional e-mail address used for cert expiration notifications (Required for ZeroSSL)."}
- {env_var: "ONLY_SUBDOMAINS", env_value: "false", desc: "If you wish to get certs only for certain subdomains, but not the main domain (main domain may be hosted on another machine and cannot be validated), set this to `true`"} - {env_var: "ONLY_SUBDOMAINS", env_value: "false", desc: "If you wish to get certs only for certain subdomains, but not the main domain (main domain may be hosted on another machine and cannot be validated), set this to `true`"}
@@ -82,6 +82,88 @@ app_setup_block: |
Set the required credentials (usually found in the plugin documentation) in `/config/dns-conf/<plugin>.ini`. Set the required credentials (usually found in the plugin documentation) in `/config/dns-conf/<plugin>.ini`.
It is recommended to attempt obtaining a certificate with `STAGING=true` first to make sure the plugin is working as expected. It is recommended to attempt obtaining a certificate with `STAGING=true` first to make sure the plugin is working as expected.
### Dynamic Reverse Proxy Configuration via Environment Variables
SWAG can dynamically generate reverse proxy configuration files directly from environment variables, bypassing the need to manage individual `.conf` files. When any `PROXY_CONFIG_*` variable is detected, this mode is activated, and any existing `.conf` files in `/config/nginx/proxy-confs/` will be removed at startup.
**Service Definition**
Each reverse proxy service is defined by an environment variable following the format `PROXY_CONFIG_<SERVICE_NAME>`. The service name will be used as the subdomain (e.g., `SERVICE_NAME.yourdomain.com`), with the special exception of `DEFAULT` (see below). The value of the variable must be a valid JSON object.
```yaml
environment:
# Configure the default site (root domain) to proxy to a dashboard service
- 'PROXY_CONFIG_DEFAULT={"name": "dashboard", "port": 80, "auth": "authelia", "quic": true}'
# Simple subdomain service
- 'PROXY_CONFIG_HOMARR={"port": 7575, "auth": "authelia"}'
# Service with a boolean flag for HTTPS backend and QUIC enabled
- 'PROXY_CONFIG_HEIMDALL={"port": 443, "https": true, "quic": true}'
# Complex service with nested objects and lists (incomplete example for syntax)
- 'PROXY_CONFIG_PLEX={
"port": 32400,
"proxy_redirect_off": true,
"buffering_off": true,
"proxy_set_headers": [
{"key": "X-Plex-Client-Identifier", "value": "$$http_x_plex_client_identifier"},
{"key": "X-Plex-Device", "value": "$$http_x_plex_device"}
],
"extra_locations": [
{"path": "/library/streams/", "custom_directives": ["proxy_pass_request_headers off"]}
]
}'
```
The available keys in the JSON object correspond to the options in the underlying Nginx template. Common keys include `port`, `https`, `quic`, `auth`, `buffering_off`, `proxy_set_headers`, and `extra_locations`.
**Configuring the Default Site (Root Domain)**
To configure the service that responds on your root domain (e.g., `https://yourdomain.com`), use the special service name `DEFAULT`.
* The environment variable is `PROXY_CONFIG_DEFAULT`.
* Unlike subdomain services, the `DEFAULT` configuration **must** include a `"name"` key in its JSON value. This key specifies the name of the container that SWAG should proxy traffic to.
* If `PROXY_CONFIG_DEFAULT` is not set, the container will serve the standard SWAG welcome page on the root domain.
Example:
```yaml
environment:
# This will proxy https://yourdomain.com to the 'dashboard' container on port 80
- 'PROXY_CONFIG_DEFAULT={"name": "dashboard", "port": 80, "auth": "none"}'
```
**Authentication Management**
Authentication can be managed globally or per-service with a clear order of precedence.
1. **Per-Service Override (Highest Priority):** Add an `auth` key directly inside the service's JSON configuration.
* `"auth": "authelia"`: Enables Authelia for this service.
* `"auth": "basic"`: Enables Basic Authentication for this service (see below).
* `"auth": "none"`: Explicitly disables authentication for this service.
2. **Global Exclusions:** A comma-separated list of service names to exclude from the global authenticator.
* `PROXY_AUTH_EXCLUDE=ntfy,public-dashboard`
3. **Global Default (Lowest Priority):** A single variable sets the default authentication provider for all services that don't have a per-service override and are not in the exclusion list.
* `PROXY_AUTH_PROVIDER=authelia` (can be `ldap`, `authentik`, etc.)
**Basic Authentication**
If you set `"auth": "basic"` for any service, you must also provide the credentials using these two environment variables. The container will automatically create the necessary `.htpasswd` file.
* `PROXY_AUTH_BASIC_USER`: The username for basic authentication.
* `PROXY_AUTH_BASIC_PASS`: The password for basic authentication.
Example:
```yaml
environment:
- 'PROXY_CONFIG_PORTAINER={"port": 9000, "auth": "basic"}'
- PROXY_AUTH_BASIC_USER=myadmin
- PROXY_AUTH_BASIC_PASS=supersecretpassword
```
### Security and password protection ### Security and password protection
* The container detects changes to url and subdomains, revokes existing certs and generates new ones during start. * The container detects changes to url and subdomains, revokes existing certs and generates new ones during start.
@@ -177,7 +259,7 @@ init_diagram: |
init-mods-end -> init-custom-files init-mods-end -> init-custom-files
init-adduser -> init-device-perms init-adduser -> init-device-perms
base -> init-envfile base -> init-envfile
init-require-url -> init-fail2ban-config init-swag-samples -> init-fail2ban-config
init-os-end -> init-folders init-os-end -> init-folders
init-php -> init-keygen init-php -> init-keygen
base -> init-migrations base -> init-migrations
@@ -198,10 +280,9 @@ init_diagram: |
init-folders -> init-samples init-folders -> init-samples
init-custom-files -> init-services init-custom-files -> init-services
init-fail2ban-config -> init-swag-config init-fail2ban-config -> init-swag-config
init-permissions -> init-swag-folders init-require-url -> init-swag-folders
init-swag-folders -> init-swag-samples init-swag-folders -> init-swag-samples
init-permissions -> init-version-checks init-permissions -> init-version-checks
init-swag-samples -> init-version-checks
init-services -> svc-cron init-services -> svc-cron
svc-cron -> legacy-services svc-cron -> legacy-services
init-services -> svc-fail2ban init-services -> svc-fail2ban
@@ -219,9 +300,7 @@ init_diagram: |
"swag:latest" <- Base Images "swag:latest" <- Base Images
# changelog # changelog
changelogs: changelogs:
- {date: "23.01.26:", desc: "Reorder init to fix proxy conf version checks."} - {date: "02.09.25:", desc: "Add ability to define proxy configurations via environment variables."}
- {date: "21.12.25:", desc: "Add support for hetzner-cloud dns validation."}
- {date: "04.11.25:", desc: "Switch default Gandi credentials from API Key to Token, allow DNS propagation time for Azure DNS plugin."}
- {date: "18.07.25:", desc: "Rebase to Alpine 3.22 with PHP 8.4. Add QUIC support. Drop PHP bindings for mcrypt as it is no longer maintained."} - {date: "18.07.25:", desc: "Rebase to Alpine 3.22 with PHP 8.4. Add QUIC support. Drop PHP bindings for mcrypt as it is no longer maintained."}
- {date: "05.05.25:", desc: "Disable Certbot's built in log rotation."} - {date: "05.05.25:", desc: "Disable Certbot's built in log rotation."}
- {date: "19.01.25:", desc: "Add [Auto Reload](https://github.com/linuxserver/docker-mods/tree/swag-auto-reload) functionality to SWAG."} - {date: "19.01.25:", desc: "Add [Auto Reload](https://github.com/linuxserver/docker-mods/tree/swag-auto-reload) functionality to SWAG."}

View File

@@ -0,0 +1,153 @@
import os
import json
import subprocess
from jinja2 import Environment, FileSystemLoader
# --- Configuration ---
TEMPLATE_DIR = '/app/config-generator/templates'
PROXY_OUTPUT_DIR = '/config/nginx/env-proxy-confs'
DEFAULT_CONF_OUTPUT = '/config/nginx/site-confs/default.conf'
HTPASSWD_FILE = '/config/nginx/.htpasswd'
# ---------------------
def process_service_config(service_name, service_config_json, global_auth_provider, auth_exclude_list):
"""Processes a single service configuration, including auth logic."""
service_config = json.loads(service_config_json)
# The default service doesn't have a subdomain name in the traditional sense
if service_name.lower() == 'default':
# We still need a target container name, let the user define it or raise an error
if 'name' not in service_config:
raise ValueError("PROXY_CONFIG_DEFAULT must contain a 'name' key specifying the target container name.")
else:
service_config['name'] = service_name
# --- Authentication Logic ---
auth_provider = 'none' # Default
# 1. Per-service override
if 'auth' in service_config:
auth_provider = service_config['auth']
print(f" - Found per-service auth override: '{auth_provider}'")
# 2. Global provider check
elif global_auth_provider and service_name not in auth_exclude_list:
auth_provider = global_auth_provider
print(f" - Applying global auth provider: '{auth_provider}'")
# 3. Otherwise, no auth
else:
if service_name in auth_exclude_list:
print(f" - Service is in global exclude list. No auth.")
else:
print(f" - No auth provider specified.")
service_config['auth_provider'] = auth_provider
return service_config
def generate_configs():
"""
Generates Nginx config files from PROXY_CONFIG environment variables and a Jinja2 template.
"""
print("--- Starting Nginx Config Generation from Environment Variables ---")
# Ensure output directories exist
os.makedirs(PROXY_OUTPUT_DIR, exist_ok=True)
os.makedirs(os.path.dirname(DEFAULT_CONF_OUTPUT), exist_ok=True)
print(f"Output directories are ready.")
# Get global auth settings from environment variables
global_auth_provider = os.environ.get('PROXY_AUTH_PROVIDER')
auth_exclude_list = os.environ.get('PROXY_AUTH_EXCLUDE', '').split(',')
auth_exclude_list = [name.strip() for name in auth_exclude_list if name.strip()]
# Get basic auth credentials
basic_auth_user = os.environ.get('PROXY_AUTH_BASIC_USER')
basic_auth_pass = os.environ.get('PROXY_AUTH_BASIC_PASS')
basic_auth_configured = False
print(f"Global Auth Provider: {global_auth_provider}")
print(f"Auth Exclude List: {auth_exclude_list}")
# Collect and process service configurations
subdomain_services = []
default_service = None
for key, value in os.environ.items():
if key.startswith('PROXY_CONFIG_'):
service_name = key.replace('PROXY_CONFIG_', '').lower()
print(f" Processing service: {service_name}")
print(value)
try:
service_config = process_service_config(service_name, value, global_auth_provider, auth_exclude_list)
# Handle Basic Auth File Creation
if service_config['auth_provider'] == 'basic' and not basic_auth_configured:
if basic_auth_user and basic_auth_pass:
print(f" - Configuring Basic Auth with user '{basic_auth_user}'.")
try:
os.makedirs(os.path.dirname(HTPASSWD_FILE), exist_ok=True)
command = ['htpasswd', '-bc', HTPASSWD_FILE, basic_auth_user, basic_auth_pass]
subprocess.run(command, check=True, capture_output=True, text=True)
print(f" - Successfully created '{HTPASSWD_FILE}'.")
basic_auth_configured = True
except subprocess.CalledProcessError as e:
print(f" [!!] ERROR: 'htpasswd' command failed: {e.stderr}. Basic auth will not be enabled.")
service_config['auth_provider'] = 'none'
except FileNotFoundError:
print(f" [!!] ERROR: 'htpasswd' command not found. Basic auth will not be enabled.")
service_config['auth_provider'] = 'none'
else:
print(f" [!!] WARNING: 'auth: basic' is set, but PROXY_AUTH_BASIC_USER or PROXY_AUTH_BASIC_PASS is missing. Skipping auth.")
service_config['auth_provider'] = 'none'
if service_name == 'default':
default_service = service_config
else:
subdomain_services.append(service_config)
except (json.JSONDecodeError, ValueError) as e:
print(f" [!!] ERROR: Could not parse or validate config for {service_name}: {e}. Skipping.")
except Exception as e:
print(f" [!!] ERROR: An unexpected error occurred processing {service_name}: {e}. Skipping.")
# Set up Jinja2 environment
try:
env = Environment(loader=FileSystemLoader(TEMPLATE_DIR), trim_blocks=True, lstrip_blocks=True)
proxy_template = env.get_template('proxy.conf.j2')
default_template = env.get_template('default.conf.j2')
print("\nJinja2 templates loaded successfully.")
except Exception as e:
print(f"ERROR: Failed to load Jinja2 templates from '{TEMPLATE_DIR}': {e}. Exiting.")
return
# Generate default site config if specified
if default_service:
print("\n--- Generating Default Site Config ---")
try:
rendered_content = default_template.render(item=default_service)
with open(DEFAULT_CONF_OUTPUT, 'w') as f:
f.write(rendered_content)
print(f" [OK] Generated {os.path.basename(DEFAULT_CONF_OUTPUT)}")
except Exception as e:
print(f" [!!] ERROR: Failed to render or write default config: {e}")
else:
print("\n--- PROXY_CONFIG_DEFAULT not set, default site config will not be generated. ---")
# Generate subdomain proxy configs
print("\n--- Generating Subdomain Proxy Configs ---")
if not subdomain_services:
print("No subdomain services found to configure.")
for service in subdomain_services:
filename = f"{service['name']}.subdomain.conf"
output_path = os.path.join(PROXY_OUTPUT_DIR, filename)
try:
rendered_content = proxy_template.render(item=service)
with open(output_path, 'w') as f:
f.write(rendered_content)
print(f" [OK] Generated {filename}")
except Exception as e:
print(f" [!!] ERROR: Failed to render or write config for {service['name']}: {e}")
print("\n--- Generation Complete ---")
if __name__ == "__main__":
generate_configs()

View File

@@ -0,0 +1,105 @@
## Version 2025/08/28
# THIS FILE IS AUTO-GENERATED BY THE CONTAINER. DO NOT EDIT.
#
# This is the default server block, handling requests to the root domain.
# redirect all traffic to https
server {
listen 80 default_server;
listen [::]:80 default_server;
location / {
return 301 https://$host$request_uri;
}
}
# main server block
server {
listen 443 ssl default_server;
{% if item.quic %}
listen 443 quic reuseport default_server;
{% else %}
# listen 443 quic reuseport default_server;
{% endif %}
listen [::]:443 ssl default_server;
{% if item.quic %}
listen [::]:443 quic reuseport default_server;
{% else %}
# listen [::]:443 quic reuseport default_server;
{% endif %}
server_name _;
include /config/nginx/ssl.conf;
client_max_body_size {{ item.client_max_body_size | default('0') }};
{% if item.proxy_redirect_off %}
proxy_redirect off;
{% endif %}
{% if item.buffering_off %}
proxy_buffering off;
{% endif %}
{% if item.auth_provider and item.auth_provider not in ['none', 'basic'] %}
# enable for {{ item.auth_provider }}
include /config/nginx/{{ item.auth_provider }}-server.conf;
{% endif %}
location / {
{% if item.auth_provider == 'basic' %}
# enable for basic auth
auth_basic "Restricted";
auth_basic_user_file /config/nginx/.htpasswd;
{% elif item.auth_provider and item.auth_provider != 'none' %}
# enable for {{ item.auth_provider }}
include /config/nginx/{{ item.auth_provider }}-location.conf;
{% else %}
# No authentication enabled for this service.
{% endif %}
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app {{ item.name }};
set $upstream_port {{ item.port }};
set $upstream_proto {% if item.https %}https{% else %}http{% endif %};
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
{% if item.proxy_set_headers %}
{% for header in item.proxy_set_headers %}
proxy_set_header {{ header.key }} {{ header.value }};
{% endfor %}
{% endif %}
{% if item.custom_directives %}
{% for directive in item.custom_directives %}
{{ directive }};
{% endfor %}
{% endif %}
}
{% if item.extra_locations %}
{% for loc in item.extra_locations %}
location {{ loc.path }} {
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app {{ loc.app | default(item.name) }};
set $upstream_port {{ loc.port | default(item.port) }};
set $upstream_proto {% if loc.https %}https{% elif item.https and loc.https is not defined %}https{% else %}http{% endif %};
proxy_pass $upstream_proto://$upstream_app:$upstream_port{% if loc.proxy_pass_path %}{{ loc.proxy_pass_path }}{% endif %};
{% if loc.proxy_set_headers %}
{% for header in loc.proxy_set_headers %}
proxy_set_header {{ header.key }} {{ header.value }};
{% endfor %}
{% endif %}
{% if loc.custom_directives %}
{% for directive in loc.custom_directives %}
{{ directive }};
{% endfor %}
{% endif %}
}
{% endfor %}
{% endif %}
}
# enable subdomain method reverse proxy confs
include /config/nginx/proxy-confs/*.subdomain.conf;
# enable env var subdomain method reverse proxy confs
include /config/nginx/env-proxy-confs/*.subdomain.conf;

View File

@@ -0,0 +1,141 @@
## Version 2025/08/28
# THIS FILE IS AUTO-GENERATED BY THE CONTAINER. DO NOT EDIT.
#
# make sure that your {{ item.name }} container is named {{ item.name }}
# make sure that your dns has a cname set for {{ item.name }}
server {
listen 443 ssl;
{% if item.quic %}
listen 443 quic reuseport;
{% else %}
# listen 443 quic reuseport;
{% endif %}
listen [::]:443 ssl;
{% if item.quic %}
listen [::]:443 quic reuseport;
{% else %}
# listen [::]:443 quic reuseport;
{% endif %}
server_name {{ item.name }}.*;
include /config/nginx/ssl.conf;
client_max_body_size {{ item.client_max_body_size | default('0') }};
{% if item.proxy_redirect_off %}
proxy_redirect off;
{% endif %}
{% if item.buffering_off %}
proxy_buffering off;
{% endif %}
{% if item.auth_provider and item.auth_provider not in ['none', 'basic'] %}
# enable for {{ item.auth_provider }}
include /config/nginx/{{ item.auth_provider }}-server.conf;
{% endif %}
location / {
{% if item.auth_provider == 'basic' %}
# enable for basic auth
auth_basic "Restricted";
auth_basic_user_file /config/nginx/.htpasswd;
{% elif item.auth_provider and item.auth_provider != 'none' %}
# enable for {{ item.auth_provider }}
include /config/nginx/{{ item.auth_provider }}-location.conf;
{% else %}
# No authentication enabled for this service.
{% endif %}
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app {{ item.name }};
set $upstream_port {{ item.port }};
set $upstream_proto {% if item.https %}https{% else %}http{% endif %};
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
{% if item.hide_xframe %}
proxy_hide_header X-Frame-Options;
{% endif %}
{% if item.iframe_friendly %}
# Uncomment to allow loading in an iframe (i.e. Organizr)
#proxy_hide_header X-Frame-Options;
{% endif %}
{% if item.hide_x_forwarded_port %}
# Hide proxy port to prevent CSRF errors
proxy_hide_header X-Forwarded-Port;
{% endif %}
{% if item.set_x_scheme %}
proxy_set_header X-Scheme https;
{% endif %}
{% if item.websockets %}
proxy_buffering off;
proxy_socket_keepalive on;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Sec-WebSocket-Extensions $http_sec_websocket_extensions;
proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;
{% endif %}
{% if item.proxy_pass_headers %}
{% for header in item.proxy_pass_headers %}
proxy_pass_header {{ header }};
{% endfor %}
{% endif %}
{% if item.proxy_set_headers %}
{% for header in item.proxy_set_headers %}
proxy_set_header {{ header.key }} {{ header.value }};
{% endfor %}
{% endif %}
{% if item.proxy_hide_headers %}
{% for header in item.proxy_hide_headers %}
proxy_hide_header {{ header }};
{% endfor %}
{% endif %}
{% if item.add_headers %}
{% for header in item.add_headers %}
add_header {{ header.key }} "{{ header.value }}";
{% endfor %}
{% endif %}
{% if item.custom_directives %}
{% for directive in item.custom_directives %}
{{ directive }};
{% endfor %}
{% endif %}
}
{% if item.api %}
location ~ (?:/{{ item.name }})?/api {
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app {{ item.name }};
set $upstream_port {{ item.port }};
set $upstream_proto {% if item.https %}https{% else %}http{% endif %};
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
}
{% endif %}
{% if item.extra_locations %}
{% for loc in item.extra_locations %}
location ~ (?:/{{ item.name }})?{{ loc.path }} {
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app {{ loc.app | default(item.name) }};
set $upstream_port {{ loc.port | default(item.port) }};
set $upstream_proto {% if loc.https %}https{% elif item.https and loc.https is not defined %}https{% else %}http{% endif %};
proxy_pass $upstream_proto://$upstream_app:$upstream_port{% if loc.proxy_pass_path %}{{ loc.proxy_pass_path }}{% endif %};
{% if loc.proxy_set_headers %}
{% for header in loc.proxy_set_headers %}
proxy_set_header {{ header.key }} {{ header.value }};
{% endfor %}
{% endif %}
{% if loc.proxy_hide_headers %}
{% for header in loc.proxy_hide_headers %}
proxy_hide_header {{ header }};
{% endfor %}
{% endif %}
{% if loc.custom_directives %}
{% for directive in loc.custom_directives %}
{{ directive }};
{% endfor %}
{% endif %}
}
{% endfor %}
{% endif %}
}

View File

@@ -1,6 +1,7 @@
# Instructions: https://github.com/obynio/certbot-plugin-gandi#usage # Instructions: https://github.com/obynio/certbot-plugin-gandi#usage
# Replace with your Gandi Live DNS v5 Personal Access Token # Replace with your value
dns_gandi_token=TOKEN # live dns v5 api key
dns_gandi_api_key=APIKEY
# optional organization id, remove it if not used # optional organization id, remove it if not used
#dns_gandi_sharing_id=SHARINGID #dns_gandi_sharing_id=SHARINGID

View File

@@ -1,2 +0,0 @@
# Hetzner Cloud API Token
dns_hetzner_cloud_api_token = your_api_token_here

View File

@@ -1,4 +1,4 @@
## Version 2026/03/07 - Changelog: https://github.com/linuxserver/docker-swag/commits/master/root/defaults/nginx/site-confs/default.conf.sample ## Version 2025/07/18 - Changelog: https://github.com/linuxserver/docker-swag/commits/master/root/defaults/nginx/site-confs/default.conf.sample
# redirect all traffic to https # redirect all traffic to https
server { server {
@@ -36,9 +36,6 @@ server {
# enable for Authentik (requires authentik-location.conf in the location block) # enable for Authentik (requires authentik-location.conf in the location block)
#include /config/nginx/authentik-server.conf; #include /config/nginx/authentik-server.conf;
# enable for Tinyauth (requires tinyauth-location.conf in the location block)
#include /config/nginx/tinyauth-server.conf;
location / { location / {
# enable for basic auth # enable for basic auth
#auth_basic "Restricted"; #auth_basic "Restricted";
@@ -53,9 +50,6 @@ server {
# enable for Authentik (requires authentik-server.conf in the server block) # enable for Authentik (requires authentik-server.conf in the server block)
#include /config/nginx/authentik-location.conf; #include /config/nginx/authentik-location.conf;
# enable for Tinyauth (requires tinyauth-server.conf in the server block)
#include /config/nginx/tinyauth-location.conf;
try_files $uri $uri/ /index.html /index.htm /index.php$is_args$args; try_files $uri $uri/ /index.html /index.htm /index.php$is_args$args;
} }
@@ -88,3 +82,5 @@ server {
# enable subdomain method reverse proxy confs # enable subdomain method reverse proxy confs
include /config/nginx/proxy-confs/*.subdomain.conf; include /config/nginx/proxy-confs/*.subdomain.conf;
# enable env var subdomain method reverse proxy confs
include /config/nginx/env-proxy-confs/*.subdomain.conf;

View File

@@ -168,9 +168,9 @@ fi
rm -rf /config/keys/letsencrypt rm -rf /config/keys/letsencrypt
if [[ "${ONLY_SUBDOMAINS}" = "true" ]] && [[ ! "${SUBDOMAINS}" = "wildcard" ]]; then if [[ "${ONLY_SUBDOMAINS}" = "true" ]] && [[ ! "${SUBDOMAINS}" = "wildcard" ]]; then
DOMAIN="$(echo "${SUBDOMAINS}" | tr ',' ' ' | awk '{print $1}').${URL}" DOMAIN="$(echo "${SUBDOMAINS}" | tr ',' ' ' | awk '{print $1}').${URL}"
ln -s ../etc/letsencrypt/live/"${DOMAIN}" /config/keys/letsencrypt ln -s /config/etc/letsencrypt/live/"${DOMAIN}" /config/keys/letsencrypt
else else
ln -s ../etc/letsencrypt/live/"${URL}" /config/keys/letsencrypt ln -s /config/etc/letsencrypt/live/"${URL}" /config/keys/letsencrypt
fi fi
# cleanup unused csr and keys folders # cleanup unused csr and keys folders
@@ -303,7 +303,7 @@ if [[ "${VALIDATION}" = "dns" ]]; then
sed -i "/^dns-${DNSPLUGIN}-credentials\b/d" /config/etc/letsencrypt/cli.ini sed -i "/^dns-${DNSPLUGIN}-credentials\b/d" /config/etc/letsencrypt/cli.ini
fi fi
# plugins that don't support setting propagation # plugins that don't support setting propagation
if [[ "${DNSPLUGIN}" =~ ^(gandi|route53|standalone)$ ]]; then if [[ "${DNSPLUGIN}" =~ ^(azure|gandi|route53|standalone)$ ]]; then
if [[ -n "${PROPAGATION}" ]]; then echo "${DNSPLUGIN} dns plugin does not support setting propagation time"; fi if [[ -n "${PROPAGATION}" ]]; then echo "${DNSPLUGIN} dns plugin does not support setting propagation time"; fi
sed -i "/^dns-${DNSPLUGIN}-propagation-seconds\b/d" /config/etc/letsencrypt/cli.ini sed -i "/^dns-${DNSPLUGIN}-propagation-seconds\b/d" /config/etc/letsencrypt/cli.ini
fi fi

View File

@@ -42,3 +42,19 @@ fi
if [[ ! -f /config/nginx/ldap-server.conf ]]; then if [[ ! -f /config/nginx/ldap-server.conf ]]; then
cp /defaults/nginx/ldap-server.conf.sample /config/nginx/ldap-server.conf cp /defaults/nginx/ldap-server.conf.sample /config/nginx/ldap-server.conf
fi fi
# clean the env target directory to ensure a fresh start
rm -f /config/nginx/env-proxy-confs/*
# check if any PROXY_CONFIG environment variables are set
if env | grep -q "^PROXY_CONFIG_"; then
echo "INFO: Found PROXY_CONFIG environment variables. Generating Nginx configs from environment..."
# run the Python generator script
echo "INFO: Running python config generator..."
if ! python3 /app/config-generator/generate_configs.py; then
echo "ERROR: The python config generator script failed. Please check the logs above. Container will not start."
exit 1
fi
echo "INFO: Config generation complete."
else
echo "INFO: No PROXY_CONFIG variables found. User is expected to manage /config/nginx/proxy-confs/ manually."
fi

View File

@@ -6,6 +6,7 @@ mkdir -p \
/config/{fail2ban,dns-conf} \ /config/{fail2ban,dns-conf} \
/config/etc/letsencrypt/renewal-hooks \ /config/etc/letsencrypt/renewal-hooks \
/config/log/{fail2ban,letsencrypt,nginx} \ /config/log/{fail2ban,letsencrypt,nginx} \
/config/nginx/env-proxy-confs \
/config/nginx/proxy-confs \ /config/nginx/proxy-confs \
/run/fail2ban \ /run/fail2ban \
/tmp/letsencrypt /tmp/letsencrypt