├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ └── feature-request.yml └── workflows │ └── build-image.yml ├── DOCKERHUB.md ├── Dockerfile ├── LICENSE ├── README.md ├── appdefs.yml ├── rootfs ├── defaults │ └── production.json ├── etc │ ├── cont-init.d │ │ ├── 54-db-upgrade.sh │ │ └── 55-nginx-proxy-manager.sh │ └── services.d │ │ ├── app │ │ └── nginx.dep │ │ ├── cert_cleanup │ │ ├── interval │ │ └── run │ │ ├── default │ │ └── cert_cleanup.dep │ │ └── nginx │ │ ├── respawn │ │ └── run ├── opt │ └── nginx-proxy-manager │ │ └── bin │ │ ├── handle-ipv6-setting │ │ ├── lecleaner │ │ ├── mysql2sqlite │ │ └── reset-password ├── startapp.sh └── usr │ └── sbin │ └── logrotate └── src ├── bcrypt-tool └── build.sh ├── nginx-proxy-manager ├── build.sh ├── pip-install.patch └── remove-certbot-dns-oci.patch └── openresty └── build.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: jlesage 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ["https://paypal.me/JocelynLeSage", "https://www.tesla.com/referral/jocelyn4590"] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: File a bug report. 3 | title: "[Bug] Provide a short description of the bug here" 4 | labels: ["bug"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this bug report! 10 | - type: textarea 11 | attributes: 12 | label: Current Behavior 13 | description: A concise description of what you're experiencing. 14 | validations: 15 | required: true 16 | - type: textarea 17 | attributes: 18 | label: Expected Behavior 19 | description: A concise description of what you expected to happen. 20 | validations: 21 | required: false 22 | - type: textarea 23 | attributes: 24 | label: Steps To Reproduce 25 | description: Steps to reproduce the behavior. 26 | validations: 27 | required: false 28 | - type: textarea 29 | attributes: 30 | label: Environment 31 | description: | 32 | Provide details about the host running the container. 33 | Examples: 34 | - Operating system (e.g. Ubuntu, Windows, TrueNAS, openmediavault, unRAID, etc). 35 | - Version of the operating system. 36 | - CPU architecture (x86-64, arm, arm64, etc). 37 | - Model of the device, if applicable (e.g. Raspberry Pi 4B, Synology DS418, QNAP TS-364, etc). 38 | - The Docker version (output of `docker version`). 39 | - Anything else specific to your environment. Examples: 40 | - Network share (NFS, CIFS) mapped to the container. 41 | - Docker running in LXC container. 42 | - etc. 43 | - If applicable, how the UI provided by the container is access: 44 | - Browser (Chrome, Firefox, Edge, etc). 45 | - Version of the browser. 46 | - OS of the browser. 47 | - Is the container accessed through a reverse proxy. 48 | - etc. 49 | value: | 50 | - OS: 51 | - OS version: 52 | - CPU: 53 | - Docker version: 54 | - Device model: 55 | - Browser/OS: 56 | validations: 57 | required: false 58 | - type: textarea 59 | attributes: 60 | label: Container creation 61 | description: | 62 | How did you create the container ? 63 | Examples: 64 | - The `docker run` command used. 65 | - The compose file used. 66 | - Screenshots of the management tool UI (e.g. Portainer, unRAID, etc) showing container settings. 67 | validations: 68 | required: true 69 | - type: textarea 70 | attributes: 71 | label: Container log 72 | description: Please copy/paste the output of `docker logs `. 73 | render: text 74 | validations: 75 | required: true 76 | - type: textarea 77 | attributes: 78 | label: Container inspect 79 | description: | 80 | If the container is running, please provide the output of `docker inspect `. 81 | **Attention**: If you defined passwords, secrets or any sensitive information via environment variables, make sure to remove them from the output. 82 | render: text 83 | validations: 84 | required: false 85 | - type: textarea 86 | attributes: 87 | label: Anything else? 88 | description: | 89 | Anything that will give more context about the issue you are encountering. 90 | 91 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. 92 | validations: 93 | required: false 94 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Ask a question, discuss 4 | url: https://github.com/jlesage/docker-nginx-proxy-manager/discussions 5 | about: Get help using this Docker container. 6 | - name: Documentation 7 | url: https://github.com/jlesage/docker-nginx-proxy-manager#readme 8 | about: Documentation about this Docker container. 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea for this project. 3 | title: "[Feature request] Provide a short description of the feature here" 4 | labels: ["enhancement"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thank you for suggesting an idea to make this project better. 10 | - type: textarea 11 | attributes: 12 | label: Idea 13 | description: | 14 | Please describe the desired behavior, pitch your idea, or suggest improvements. 15 | validations: 16 | required: true 17 | -------------------------------------------------------------------------------- /.github/workflows/build-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker image CI/CD 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | env: 8 | DOCKER_IMAGE_NAME: jlesage/nginx-proxy-manager 9 | PLATFORMS: linux/amd64,linux/386,linux/arm/v6,linux/arm/v7,linux/arm64/v8 10 | 11 | on: 12 | push: 13 | branches: '*' 14 | tags: 15 | - v[0-9][0-9].[0-9][0-9].[0-9]+ 16 | - v[0-9][0-9].[0-9][0-9].[0-9]+-pre.[0-9]+ 17 | pull_request: 18 | 19 | jobs: 20 | build: 21 | name: Build image 22 | runs-on: ubuntu-20.04 23 | 24 | steps: 25 | - name: Free disk space 26 | run: | 27 | # Free disk space. 28 | echo "::group::Before" 29 | df -h / 30 | echo "::endgroup::" 31 | echo "::group::Removing unneeded softwares and files..." 32 | for DIR in /usr/local/lib/android /usr/share/dotnet /opt/ghc 33 | do 34 | if [ -d "$DIR" ]; then 35 | echo "Removing $DIR..." 36 | sudo rm -r "$DIR" 37 | fi 38 | done 39 | echo "::endgroup::" 40 | echo "::group::After" 41 | df -h / 42 | echo "::endgroup::" 43 | 44 | - name: Prepare 45 | id: prep 46 | run: | 47 | # Determine the Docker container version. 48 | VERSION=unknown 49 | if [[ $GITHUB_REF =~ refs/tags/* ]]; then 50 | # Git tag pushed: use tag as the version. 51 | VERSION=${GITHUB_REF#refs/tags/} 52 | elif [[ $GITHUB_REF =~ refs/heads/* ]]; then 53 | # Git commit pushed: use the commit SHA as the version. 54 | VERSION=${GITHUB_SHA::8} 55 | elif [[ $GITHUB_REF =~ refs/pull/* ]]; then 56 | # Pull request: use PR number as the version. 57 | VERSION=pr-${{ github.event.number }} 58 | else 59 | echo "::error::Unexpected GITHUB_REF: $GITHUB_REF" 60 | exit 1 61 | fi 62 | # Determine the version to put in container label. 63 | LABEL_VERSION=${VERSION} 64 | if [[ $GITHUB_REF =~ refs/tags/* ]]; then 65 | # Do not include the starting 'v' of the version. 66 | LABEL_VERSION=${VERSION:1} 67 | fi 68 | # Determine the Docker container tags. 69 | TAGS="${{ env.DOCKER_IMAGE_NAME }}:${VERSION}" 70 | if [[ $GITHUB_REF =~ refs/tags/* ]]; then 71 | TAGS="$TAGS,${{ env.DOCKER_IMAGE_NAME }}:latest" 72 | fi 73 | # Determine the release type. 74 | if [[ $GITHUB_REF =~ refs/tags/* ]]; then 75 | IS_RELEASE=yes 76 | if [[ $GITHUB_REF =~ -pre\.[0-9]+ ]]; then 77 | RELEASE_TYPE="pre" 78 | else 79 | RELEASE_TYPE="standard" 80 | fi 81 | else 82 | IS_RELEASE=no 83 | RELEASE_TYPE="n/a" 84 | fi 85 | # Print results. 86 | echo "::group::Results" 87 | echo "Github reference: $GITHUB_REF" 88 | echo "Release: $IS_RELEASE" 89 | echo "Release type: $RELEASE_TYPE" 90 | echo "Docker container version: $VERSION" 91 | echo "Docker container version label: $LABEL_VERSION" 92 | echo "Docker container tag(s): $TAGS" 93 | echo "::endgroup::" 94 | # Export outputs. 95 | echo "is_release=${IS_RELEASE}" >> $GITHUB_OUTPUT 96 | echo "release_type=${RELEASE_TYPE}" >> $GITHUB_OUTPUT 97 | echo "version=${VERSION}" >> $GITHUB_OUTPUT 98 | echo "label_version=${LABEL_VERSION}" >> $GITHUB_OUTPUT 99 | echo "tags=${TAGS}" >> $GITHUB_OUTPUT 100 | #echo "build_date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT 101 | 102 | - name: Setup QEMU 103 | uses: docker/setup-qemu-action@v3 104 | with: 105 | platforms: arm,arm64,ppc64le,mips64,s390x 106 | 107 | - name: Setup Docker Buildx 108 | uses: docker/setup-buildx-action@v3 109 | 110 | - name: Login to DockerHub 111 | if: ${{ steps.prep.outputs.is_release == 'yes' }} 112 | uses: docker/login-action@v3 113 | with: 114 | username: ${{ secrets.DOCKERHUB_USERNAME }} 115 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 116 | 117 | - name: Build and push 118 | uses: docker/build-push-action@v5 119 | with: 120 | push: ${{ steps.prep.outputs.is_release == 'yes' }} 121 | provenance: false 122 | platforms: ${{ env.PLATFORMS }} 123 | tags: ${{ steps.prep.outputs.tags }} 124 | build-args: | 125 | DOCKER_IMAGE_VERSION=${{ steps.prep.outputs.label_version }} 126 | cache-from: type=gha,scope=${{ env.DOCKER_IMAGE_NAME }} 127 | cache-to: type=gha,mode=max,scope=${{ env.DOCKER_IMAGE_NAME }} 128 | 129 | - name: Inspect 130 | if: ${{ steps.prep.outputs.is_release == 'yes' }} 131 | run: | 132 | docker buildx imagetools inspect ${{ env.DOCKER_IMAGE_NAME }}:${{ steps.prep.outputs.version }} 133 | 134 | - name: Checkout 135 | uses: actions/checkout@v4 136 | if: ${{ steps.prep.outputs.release_type == 'standard' }} 137 | 138 | - name: Dockerhub description 139 | if: ${{ steps.prep.outputs.release_type == 'standard' }} 140 | uses: peter-evans/dockerhub-description@v4 141 | with: 142 | username: ${{ secrets.DOCKERHUB_USERNAME }} 143 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 144 | repository: ${{ env.DOCKER_IMAGE_NAME }} 145 | readme-filepath: DOCKERHUB.md 146 | 147 | notification: 148 | name: Notification 149 | needs: [ build ] 150 | runs-on: ubuntu-20.04 151 | if: ${{ always() && github.event_name != 'pull_request' }} 152 | 153 | steps: 154 | - name: Pushover notification 155 | uses: desiderati/github-action-pushover@v1 156 | with: 157 | job-status: ${{ needs.build.result }} 158 | pushover-api-token: ${{ secrets.PUSHOVER_API_TOKEN }} 159 | pushover-user-key: ${{ secrets.PUSHOVER_USER_KEY }} 160 | -------------------------------------------------------------------------------- /DOCKERHUB.md: -------------------------------------------------------------------------------- 1 | # Docker container for Nginx Proxy Manager 2 | [![Release](https://img.shields.io/github/release/jlesage/docker-nginx-proxy-manager.svg?logo=github&style=for-the-badge)](https://github.com/jlesage/docker-nginx-proxy-manager/releases/latest) 3 | [![Docker Image Size](https://img.shields.io/docker/image-size/jlesage/nginx-proxy-manager/latest?logo=docker&style=for-the-badge)](https://hub.docker.com/r/jlesage/nginx-proxy-manager/tags) 4 | [![Docker Pulls](https://img.shields.io/docker/pulls/jlesage/nginx-proxy-manager?label=Pulls&logo=docker&style=for-the-badge)](https://hub.docker.com/r/jlesage/nginx-proxy-manager) 5 | [![Docker Stars](https://img.shields.io/docker/stars/jlesage/nginx-proxy-manager?label=Stars&logo=docker&style=for-the-badge)](https://hub.docker.com/r/jlesage/nginx-proxy-manager) 6 | [![Build Status](https://img.shields.io/github/actions/workflow/status/jlesage/docker-nginx-proxy-manager/build-image.yml?logo=github&branch=master&style=for-the-badge)](https://github.com/jlesage/docker-nginx-proxy-manager/actions/workflows/build-image.yml) 7 | [![Source](https://img.shields.io/badge/Source-GitHub-blue?logo=github&style=for-the-badge)](https://github.com/jlesage/docker-nginx-proxy-manager) 8 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg?style=for-the-badge)](https://paypal.me/JocelynLeSage) 9 | 10 | This is a Docker container for [Nginx Proxy Manager](https://nginxproxymanager.com). 11 | 12 | 13 | 14 | --- 15 | 16 | [![Nginx Proxy Manager logo](https://images.weserv.nl/?url=raw.githubusercontent.com/jlesage/docker-templates/master/jlesage/images/nginx-proxy-manager-icon.png&w=110)](https://nginxproxymanager.com)[![Nginx Proxy Manager](https://images.placeholders.dev/?width=608&height=110&fontFamily=monospace&fontWeight=400&fontSize=52&text=Nginx%20Proxy%20Manager&bgColor=rgba(0,0,0,0.0)&textColor=rgba(121,121,121,1))](https://nginxproxymanager.com) 17 | 18 | Nginx Proxy Manager enables you to easily forward to your websites running at 19 | home or otherwise, including free SSL, without having to know too much about 20 | Nginx or Letsencrypt. 21 | 22 | --- 23 | 24 | ## Quick Start 25 | 26 | **NOTE**: 27 | The Docker command provided in this quick start is given as an example 28 | and parameters should be adjusted to your need. 29 | 30 | Launch the Nginx Proxy Manager docker container with the following command: 31 | ```shell 32 | docker run -d \ 33 | --name=nginx-proxy-manager \ 34 | -p 8181:8181 \ 35 | -p 8080:8080 \ 36 | -p 4443:4443 \ 37 | -v /docker/appdata/nginx-proxy-manager:/config:rw \ 38 | jlesage/nginx-proxy-manager 39 | ``` 40 | 41 | Where: 42 | 43 | - `/docker/appdata/nginx-proxy-manager`: This is where the application stores its configuration, states, log and any files needing persistency. 44 | 45 | Browse to `http://your-host-ip:8181` to access the Nginx Proxy Manager web interface. 46 | 47 | ## Documentation 48 | 49 | Full documentation is available at https://github.com/jlesage/docker-nginx-proxy-manager. 50 | 51 | ## Support or Contact 52 | 53 | Having troubles with the container or have questions? Please 54 | [create a new issue]. 55 | 56 | For other great Dockerized applications, see https://jlesage.github.io/docker-apps. 57 | 58 | [create a new issue]: https://github.com/jlesage/docker-nginx-proxy-manager/issues 59 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # nginx-proxy-manager Dockerfile 3 | # 4 | # https://github.com/jlesage/docker-nginx-proxy-manager 5 | # 6 | 7 | # Docker image version is provided via build arg. 8 | ARG DOCKER_IMAGE_VERSION= 9 | 10 | # Define software versions. 11 | ARG OPENRESTY_VERSION=1.19.9.1 12 | ARG NGINX_PROXY_MANAGER_VERSION=2.11.3 13 | ARG NGINX_HTTP_GEOIP2_MODULE_VERSION=3.3 14 | ARG LIBMAXMINDDB_VERSION=1.5.0 15 | ARG BCRYPT_TOOL_VERSION=1.1.2 16 | 17 | # Define software download URLs. 18 | ARG OPENRESTY_URL=https://openresty.org/download/openresty-${OPENRESTY_VERSION}.tar.gz 19 | ARG NGINX_PROXY_MANAGER_URL=https://github.com/jc21/nginx-proxy-manager/archive/v${NGINX_PROXY_MANAGER_VERSION}.tar.gz 20 | ARG NGINX_HTTP_GEOIP2_MODULE_URL=https://github.com/leev/ngx_http_geoip2_module/archive/${NGINX_HTTP_GEOIP2_MODULE_VERSION}.tar.gz 21 | ARG LIBMAXMINDDB_URL=https://github.com/maxmind/libmaxminddb/releases/download/${LIBMAXMINDDB_VERSION}/libmaxminddb-${LIBMAXMINDDB_VERSION}.tar.gz 22 | 23 | # Get Dockerfile cross-compilation helpers. 24 | FROM --platform=$BUILDPLATFORM tonistiigi/xx AS xx 25 | 26 | # Get Python cryptography wheel. It is needed for certbot. 27 | FROM moonbuggy2000/python-musl-wheels:cryptography41.0.3-py3.10-${TARGETARCH}${TARGETVARIANT} AS mod_cryptography 28 | 29 | # Build UPX. 30 | FROM --platform=$BUILDPLATFORM alpine:3.16 AS upx 31 | RUN apk --no-cache add build-base curl make cmake git && \ 32 | mkdir /tmp/upx && \ 33 | curl -# -L https://github.com/upx/upx/releases/download/v4.0.1/upx-4.0.1-src.tar.xz | tar xJ --strip 1 -C /tmp/upx && \ 34 | make -C /tmp/upx build/release-gcc -j$(nproc) && \ 35 | cp -v /tmp/upx/build/release-gcc/upx /usr/bin/upx 36 | 37 | # Build Nginx Proxy Manager. 38 | FROM --platform=$BUILDPLATFORM alpine:3.16 AS npm 39 | ARG TARGETPLATFORM 40 | ARG NGINX_PROXY_MANAGER_VERSION 41 | ARG NGINX_PROXY_MANAGER_URL 42 | COPY --from=xx / / 43 | COPY src/nginx-proxy-manager /build 44 | RUN /build/build.sh "$NGINX_PROXY_MANAGER_VERSION" "$NGINX_PROXY_MANAGER_URL" 45 | 46 | # Build OpenResty (nginx). 47 | FROM --platform=$BUILDPLATFORM alpine:3.16 AS nginx 48 | ARG TARGETPLATFORM 49 | ARG OPENRESTY_URL 50 | ARG NGINX_HTTP_GEOIP2_MODULE_URL 51 | ARG LIBMAXMINDDB_URL 52 | COPY --from=xx / / 53 | COPY src/openresty /build 54 | RUN /build/build.sh "$OPENRESTY_URL" "$NGINX_HTTP_GEOIP2_MODULE_URL" "$LIBMAXMINDDB_URL" 55 | RUN xx-verify /tmp/openresty-install/usr/sbin/nginx 56 | 57 | # Build bcrypt-tool. 58 | FROM --platform=$BUILDPLATFORM alpine:3.16 AS bcrypt-tool 59 | ARG TARGETPLATFORM 60 | ARG BCRYPT_TOOL_VERSION 61 | COPY --from=xx / / 62 | COPY src/bcrypt-tool /build 63 | RUN /build/build.sh "$BCRYPT_TOOL_VERSION" 64 | RUN xx-verify /tmp/go/bin/bcrypt-tool 65 | COPY --from=upx /usr/bin/upx /usr/bin/upx 66 | RUN upx /tmp/go/bin/bcrypt-tool 67 | 68 | # Build certbot. 69 | FROM alpine:3.16 AS certbot 70 | COPY --from=mod_cryptography / /wheels 71 | RUN \ 72 | apk --no-cache add build-base curl python3 && \ 73 | curl -# -L "https://bootstrap.pypa.io/get-pip.py" | python3 && \ 74 | pip install --no-cache-dir --root=/tmp/certbot-install --prefix=/usr --find-links /wheels/ --prefer-binary --only-binary=:all: certbot && \ 75 | find /tmp/certbot-install/usr/lib/python3.10/site-packages -type f -name "*.so" -exec strip {} ';' && \ 76 | find /tmp/certbot-install/usr/lib/python3.10/site-packages -type f -name "*.h" -delete && \ 77 | find /tmp/certbot-install/usr/lib/python3.10/site-packages -type f -name "*.c" -delete && \ 78 | find /tmp/certbot-install/usr/lib/python3.10/site-packages -type f -name "*.exe" -delete && \ 79 | find /tmp/certbot-install/usr/lib/python3.10/site-packages -type d -name tests -print0 | xargs -0 rm -r 80 | 81 | # Pull base image. 82 | FROM jlesage/baseimage:alpine-3.16-v3.6.2 83 | 84 | ARG NGINX_PROXY_MANAGER_VERSION 85 | ARG DOCKER_IMAGE_VERSION 86 | 87 | # Define working directory. 88 | WORKDIR /tmp 89 | 90 | # Install dependencies. 91 | RUN \ 92 | add-pkg \ 93 | curl \ 94 | nodejs \ 95 | python3 \ 96 | sqlite \ 97 | openssl \ 98 | apache2-utils \ 99 | # For /opt/nginx-proxy-manager/bin/handle-ipv6-setting. 100 | bash \ 101 | # For openresty. 102 | pcre \ 103 | luajit \ 104 | && \ 105 | # Install pip. 106 | # NOTE: pip from the Alpine package repository is debundled, meaning that 107 | # its dependencies are part of the system-wide ones. This save a lot 108 | # of space, but these dependencies conflict with the ones required by 109 | # Certbot plugins. Thus, we need to manually install pip (with its 110 | # built-in dependencies). See: 111 | # https://pip.pypa.io/en/stable/development/vendoring-policy/ 112 | curl -# -L "https://bootstrap.pypa.io/get-pip.py" | python3 113 | 114 | # Add files. 115 | COPY rootfs/ / 116 | COPY --from=nginx /tmp/openresty-install/ / 117 | COPY --from=npm /tmp/nginx-proxy-manager-install/ / 118 | COPY --from=bcrypt-tool /tmp/go/bin/bcrypt-tool /usr/bin/ 119 | COPY --from=certbot /tmp/certbot-install/ / 120 | 121 | # Set internal environment variables. 122 | RUN \ 123 | set-cont-env APP_NAME "Nginx Proxy Manager" && \ 124 | set-cont-env APP_VERSION "$NGINX_PROXY_MANAGER_VERSION" && \ 125 | set-cont-env DOCKER_IMAGE_VERSION "$DOCKER_IMAGE_VERSION" && \ 126 | true 127 | 128 | # Set public environment variables. 129 | ENV \ 130 | DISABLE_IPV6=0 131 | 132 | # Expose ports. 133 | # - 8080: HTTP traffic 134 | # - 4443: HTTPs traffic 135 | # - 8181: Management web interface 136 | EXPOSE 8080 4443 8181 137 | 138 | # Metadata. 139 | LABEL \ 140 | org.label-schema.name="nginx-proxy-manager" \ 141 | org.label-schema.description="Docker container for Nginx Proxy Manager" \ 142 | org.label-schema.version="${DOCKER_IMAGE_VERSION:-unknown}" \ 143 | org.label-schema.vcs-url="https://github.com/jlesage/docker-nginx-proxy-manager" \ 144 | org.label-schema.schema-version="1.0" 145 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Jocelyn Le Sage 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Note 2 | 3 | This is a drop in replacement for [jlesage/nginx-proxy-manager](https://hub.docker.com/r/jlesage/nginx-proxy-manager) 4 | 5 | This fork includes the [OpenResty Crowdsec Bouncer](https://github.com/crowdsecurity/cs-openresty-bouncer) 6 | 7 | Please see the [crowdsec_support](https://github.com/LePresidente/docker-nginx-proxy-manager/tree/crowdsec_support) branch for the changes as 8 | 9 | Docker images hosted on dockerhub. 10 | 11 | https://hub.docker.com/r/lepresidente/nginx-proxy-manager 12 | 13 | | TAG | cs-openresty-bouncer version| 14 | |-----------|-----------------------------| 15 | | latest | 0.1.10 (PreRelease) | 16 | 17 | 18 | Instructions to use: 19 | Starting the container at this point will start Nginx-Proxy-Manager as before but will create a new file in /config/crowdsec/ called crowdsec-openresty-bouncer.conf 20 | 21 | You will need to edit this file with at least the following changes then restart the container. 22 | 23 | ``` 24 | ENABLED=true 25 | API_URL=http://:8080 26 | API_KEY= 27 | ``` 28 | 29 | the crowdsec api key can be generated on the crowdsec instance using the following command 30 | 31 | ``` 32 | cscli bouncers add npm-proxy 33 | ``` 34 | 35 | Currently this is a side project and I will try keep this up to date 36 | 37 | # Docker container for Nginx Proxy Manager 38 | [![Release](https://img.shields.io/github/release/jlesage/docker-nginx-proxy-manager.svg?logo=github&style=for-the-badge)](https://github.com/jlesage/docker-nginx-proxy-manager/releases/latest) 39 | [![Docker Image Size](https://img.shields.io/docker/image-size/jlesage/nginx-proxy-manager/latest?logo=docker&style=for-the-badge)](https://hub.docker.com/r/jlesage/nginx-proxy-manager/tags) 40 | [![Docker Pulls](https://img.shields.io/docker/pulls/jlesage/nginx-proxy-manager?label=Pulls&logo=docker&style=for-the-badge)](https://hub.docker.com/r/jlesage/nginx-proxy-manager) 41 | [![Docker Stars](https://img.shields.io/docker/stars/jlesage/nginx-proxy-manager?label=Stars&logo=docker&style=for-the-badge)](https://hub.docker.com/r/jlesage/nginx-proxy-manager) 42 | [![Build Status](https://img.shields.io/github/actions/workflow/status/jlesage/docker-nginx-proxy-manager/build-image.yml?logo=github&branch=master&style=for-the-badge)](https://github.com/jlesage/docker-nginx-proxy-manager/actions/workflows/build-image.yml) 43 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg?style=for-the-badge)](https://paypal.me/JocelynLeSage) 44 | 45 | This project implements a Docker container for [Nginx Proxy Manager](https://nginxproxymanager.com). 46 | 47 | 48 | 49 | --- 50 | 51 | [![Nginx Proxy Manager logo](https://images.weserv.nl/?url=raw.githubusercontent.com/jlesage/docker-templates/master/jlesage/images/nginx-proxy-manager-icon.png&w=110)](https://nginxproxymanager.com)[![Nginx Proxy Manager](https://images.placeholders.dev/?width=608&height=110&fontFamily=monospace&fontWeight=400&fontSize=52&text=Nginx%20Proxy%20Manager&bgColor=rgba(0,0,0,0.0)&textColor=rgba(121,121,121,1))](https://nginxproxymanager.com) 52 | 53 | Nginx Proxy Manager enables you to easily forward to your websites running at 54 | home or otherwise, including free SSL, without having to know too much about 55 | Nginx or Letsencrypt. 56 | 57 | --- 58 | 59 | ## Table of Content 60 | 61 | * [Quick Start](#quick-start) 62 | * [Usage](#usage) 63 | * [Environment Variables](#environment-variables) 64 | * [Deployment Considerations](#deployment-considerations) 65 | * [Data Volumes](#data-volumes) 66 | * [Ports](#ports) 67 | * [Changing Parameters of a Running Container](#changing-parameters-of-a-running-container) 68 | * [Docker Compose File](#docker-compose-file) 69 | * [Docker Image Versioning](#docker-image-versioning) 70 | * [Docker Image Update](#docker-image-update) 71 | * [Synology](#synology) 72 | * [unRAID](#unraid) 73 | * [User/Group IDs](#usergroup-ids) 74 | * [Accessing the GUI](#accessing-the-gui) 75 | * [Shell Access](#shell-access) 76 | * [Default Administrator Account](#default-administrator-account) 77 | * [Accessibility From The Internet](#accessibility-from-the-internet) 78 | * [Troubleshooting](#troubleshooting) 79 | * [Password Reset](#password-reset) 80 | * [Support or Contact](#support-or-contact) 81 | 82 | ## Quick Start 83 | 84 | **NOTE**: 85 | The Docker command provided in this quick start is given as an example 86 | and parameters should be adjusted to your need. 87 | 88 | Launch the Nginx Proxy Manager docker container with the following command: 89 | ```shell 90 | docker run -d \ 91 | --name=nginx-proxy-manager \ 92 | -p 8181:8181 \ 93 | -p 8080:8080 \ 94 | -p 4443:4443 \ 95 | -v /docker/appdata/nginx-proxy-manager:/config:rw \ 96 | jlesage/nginx-proxy-manager 97 | ``` 98 | 99 | Where: 100 | 101 | - `/docker/appdata/nginx-proxy-manager`: This is where the application stores its configuration, states, log and any files needing persistency. 102 | 103 | Browse to `http://your-host-ip:8181` to access the Nginx Proxy Manager web interface. 104 | 105 | ## Usage 106 | 107 | ```shell 108 | docker run [-d] \ 109 | --name=nginx-proxy-manager \ 110 | [-e =]... \ 111 | [-v :[:PERMISSIONS]]... \ 112 | [-p :]... \ 113 | jlesage/nginx-proxy-manager 114 | ``` 115 | 116 | | Parameter | Description | 117 | |-----------|-------------| 118 | | -d | Run the container in the background. If not set, the container runs in the foreground. | 119 | | -e | Pass an environment variable to the container. See the [Environment Variables](#environment-variables) section for more details. | 120 | | -v | Set a volume mapping (allows to share a folder/file between the host and the container). See the [Data Volumes](#data-volumes) section for more details. | 121 | | -p | Set a network port mapping (exposes an internal container port to the host). See the [Ports](#ports) section for more details. | 122 | 123 | ### Environment Variables 124 | 125 | To customize some properties of the container, the following environment 126 | variables can be passed via the `-e` parameter (one for each variable). Value 127 | of this parameter has the format `=`. 128 | 129 | | Variable | Description | Default | 130 | |----------------|----------------------------------------------|---------| 131 | |`USER_ID`| ID of the user the application runs as. See [User/Group IDs](#usergroup-ids) to better understand when this should be set. | `1000` | 132 | |`GROUP_ID`| ID of the group the application runs as. See [User/Group IDs](#usergroup-ids) to better understand when this should be set. | `1000` | 133 | |`SUP_GROUP_IDS`| Comma-separated list of supplementary group IDs of the application. | (no value) | 134 | |`UMASK`| Mask that controls how permissions are set for newly created files and folders. The value of the mask is in octal notation. By default, the default umask value is `0022`, meaning that newly created files and folders are readable by everyone, but only writable by the owner. See the online umask calculator at http://wintelguy.com/umask-calc.pl. | `0022` | 135 | |`LANG`| Set the [locale](https://en.wikipedia.org/wiki/Locale_(computer_software)), which defines the application's language, **if supported**. Format of the locale is `language[_territory][.codeset]`, where language is an [ISO 639 language code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes), territory is an [ISO 3166 country code](https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes) and codeset is a character set, like `UTF-8`. For example, Australian English using the UTF-8 encoding is `en_AU.UTF-8`. | `en_US.UTF-8` | 136 | |`TZ`| [TimeZone](http://en.wikipedia.org/wiki/List_of_tz_database_time_zones) used by the container. Timezone can also be set by mapping `/etc/localtime` between the host and the container. | `Etc/UTC` | 137 | |`KEEP_APP_RUNNING`| When set to `1`, the application will be automatically restarted when it crashes or terminates. | `0` | 138 | |`APP_NICENESS`| Priority at which the application should run. A niceness value of -20 is the highest priority and 19 is the lowest priority. The default niceness value is 0. **NOTE**: A negative niceness (priority increase) requires additional permissions. In this case, the container should be run with the docker option `--cap-add=SYS_NICE`. | `0` | 139 | |`INSTALL_PACKAGES`| Space-separated list of packages to install during the startup of the container. List of available packages can be found at https://mirrors.alpinelinux.org. **ATTENTION**: Container functionality can be affected when installing a package that overrides existing container files (e.g. binaries). | (no value) | 140 | |`PACKAGES_MIRROR`| Mirror of the repository to use when installing packages. List of mirrors is available at https://mirrors.alpinelinux.org. | (no value) | 141 | |`CONTAINER_DEBUG`| Set to `1` to enable debug logging. | `0` | 142 | |`DISABLE_IPV6`| When set to `1`, IPv6 support is disabled. This is needed when IPv6 is not enabled/supported on the host. | `0` | 143 | 144 | #### Deployment Considerations 145 | 146 | Many tools used to manage Docker containers extract environment variables 147 | defined by the Docker image and use them to create/deploy the container. For 148 | example, this is done by: 149 | - The Docker application on Synology NAS 150 | - The Container Station on QNAP NAS 151 | - Portainer 152 | - etc. 153 | 154 | While this can be useful for the user to adjust the value of environment 155 | variables to fit its needs, it can also be confusing and dangerous to keep all 156 | of them. 157 | 158 | A good practice is to set/keep only the variables that are needed for the 159 | container to behave as desired in a specific setup. If the value of variable is 160 | kept to its default value, it means that it can be removed. Keep in mind that 161 | all variables are optional, meaning that none of them is required for the 162 | container to start. 163 | 164 | Removing environment variables that are not needed provides some advantages: 165 | 166 | - Prevents keeping variables that are no longer used by the container. Over 167 | time, with image updates, some variables might be removed. 168 | - Allows the Docker image to change/fix a default value. Again, with image 169 | updates, the default value of a variable might be changed to fix an issue, 170 | or to better support a new feature. 171 | - Prevents changes to a variable that might affect the correct function of 172 | the container. Some undocumented variables, like `PATH` or `ENV`, are 173 | required to be exposed, but are not meant to be changed by users. However, 174 | container management tools still show these variables to users. 175 | - There is a bug with the Container Station on QNAP and the Docker application 176 | on Synology, where an environment variable without value might not be 177 | allowed. This behavior is wrong: it's absolutely fine to have a variable 178 | without value. In fact, this container does have variables without value by 179 | default. Thus, removing unneeded variables is a good way to prevent 180 | deployment issue on these devices. 181 | 182 | ### Data Volumes 183 | 184 | The following table describes data volumes used by the container. The mappings 185 | are set via the `-v` parameter. Each mapping is specified with the following 186 | format: `:[:PERMISSIONS]`. 187 | 188 | | Container path | Permissions | Description | 189 | |-----------------|-------------|-------------| 190 | |`/config`| rw | This is where the application stores its configuration, states, log and any files needing persistency. | 191 | 192 | ### Ports 193 | 194 | Here is the list of ports used by the container. 195 | 196 | When using the default bridge network, ports can be mapped to the host via the 197 | `-p` parameter (one per port mapping). Each mapping is defined with the 198 | following format: `:`. The port number used inside 199 | the container might not be changeable, but you are free to use any port on the 200 | host side. 201 | 202 | See the [Docker Container Networking](https://docs.docker.com/config/containers/container-networking) 203 | documentation for more details. 204 | 205 | | Port | Protocol | Mapping to host | Description | 206 | |------|----------|-----------------|-------------| 207 | | 8181 | TCP | Mandatory | Port used to access the web interface of the application. | 208 | | 8080 | TCP | Mandatory | Port used to serve HTTP requests. | 209 | | 4443 | TCP | Mandatory | Port used to serve HTTPs requests. | 210 | 211 | ### Changing Parameters of a Running Container 212 | 213 | As can be seen, environment variables, volume and port mappings are all specified 214 | while creating the container. 215 | 216 | The following steps describe the method used to add, remove or update 217 | parameter(s) of an existing container. The general idea is to destroy and 218 | re-create the container: 219 | 220 | 1. Stop the container (if it is running): 221 | ```shell 222 | docker stop nginx-proxy-manager 223 | ``` 224 | 225 | 2. Remove the container: 226 | ```shell 227 | docker rm nginx-proxy-manager 228 | ``` 229 | 230 | 3. Create/start the container using the `docker run` command, by adjusting 231 | parameters as needed. 232 | 233 | **NOTE**: 234 | Since all application's data is saved under the `/config` container 235 | folder, destroying and re-creating a container is not a problem: nothing is 236 | lost and the application comes back with the same state (as long as the 237 | mapping of the `/config` folder remains the same). 238 | 239 | ## Docker Compose File 240 | 241 | Here is an example of a `docker-compose.yml` file that can be used with 242 | [Docker Compose](https://docs.docker.com/compose/overview/). 243 | 244 | Make sure to adjust according to your needs. Note that only mandatory network 245 | ports are part of the example. 246 | 247 | ```yaml 248 | version: '3' 249 | services: 250 | nginx-proxy-manager: 251 | image: jlesage/nginx-proxy-manager 252 | ports: 253 | - "8181:8181" 254 | - "8080:8080" 255 | - "4443:4443" 256 | volumes: 257 | - "/docker/appdata/nginx-proxy-manager:/config:rw" 258 | ``` 259 | 260 | ## Docker Image Versioning 261 | 262 | Each release of a Docker image is versioned. Prior to october 2022, the 263 | [semantic versioning](https://semver.org) was used as the versioning scheme. 264 | 265 | Since then, versioning scheme changed to 266 | [calendar versioning](https://calver.org). The format used is `YY.MM.SEQUENCE`, 267 | where: 268 | - `YY` is the zero-padded year (relative to year 2000). 269 | - `MM` is the zero-padded month. 270 | - `SEQUENCE` is the incremental release number within the month (first release 271 | is 1, second is 2, etc). 272 | 273 | ## Docker Image Update 274 | 275 | Because features are added, issues are fixed, or simply because a new version 276 | of the containerized application is integrated, the Docker image is regularly 277 | updated. Different methods can be used to update the Docker image. 278 | 279 | The system used to run the container may have a built-in way to update 280 | containers. If so, this could be your primary way to update Docker images. 281 | 282 | An other way is to have the image be automatically updated with [Watchtower]. 283 | Watchtower is a container-based solution for automating Docker image updates. 284 | This is a "set and forget" type of solution: once a new image is available, 285 | Watchtower will seamlessly perform the necessary steps to update the container. 286 | 287 | Finally, the Docker image can be manually updated with these steps: 288 | 289 | 1. Fetch the latest image: 290 | ```shell 291 | docker pull jlesage/nginx-proxy-manager 292 | ``` 293 | 294 | 2. Stop the container: 295 | ```shell 296 | docker stop nginx-proxy-manager 297 | ``` 298 | 299 | 3. Remove the container: 300 | ```shell 301 | docker rm nginx-proxy-manager 302 | ``` 303 | 304 | 4. Create and start the container using the `docker run` command, with the 305 | the same parameters that were used when it was deployed initially. 306 | 307 | [Watchtower]: https://github.com/containrrr/watchtower 308 | 309 | ### Synology 310 | 311 | For owners of a Synology NAS, the following steps can be used to update a 312 | container image. 313 | 314 | 1. Open the *Docker* application. 315 | 2. Click on *Registry* in the left pane. 316 | 3. In the search bar, type the name of the container (`jlesage/nginx-proxy-manager`). 317 | 4. Select the image, click *Download* and then choose the `latest` tag. 318 | 5. Wait for the download to complete. A notification will appear once done. 319 | 6. Click on *Container* in the left pane. 320 | 7. Select your Nginx Proxy Manager container. 321 | 8. Stop it by clicking *Action*->*Stop*. 322 | 9. Clear the container by clicking *Action*->*Reset* (or *Action*->*Clear* if 323 | you don't have the latest *Docker* application). This removes the 324 | container while keeping its configuration. 325 | 10. Start the container again by clicking *Action*->*Start*. **NOTE**: The 326 | container may temporarily disappear from the list while it is re-created. 327 | 328 | ### unRAID 329 | 330 | For unRAID, a container image can be updated by following these steps: 331 | 332 | 1. Select the *Docker* tab. 333 | 2. Click the *Check for Updates* button at the bottom of the page. 334 | 3. Click the *update ready* link of the container to be updated. 335 | 336 | ## User/Group IDs 337 | 338 | When using data volumes (`-v` flags), permissions issues can occur between the 339 | host and the container. For example, the user within the container may not 340 | exist on the host. This could prevent the host from properly accessing files 341 | and folders on the shared volume. 342 | 343 | To avoid any problem, you can specify the user the application should run as. 344 | 345 | This is done by passing the user ID and group ID to the container via the 346 | `USER_ID` and `GROUP_ID` environment variables. 347 | 348 | To find the right IDs to use, issue the following command on the host, with the 349 | user owning the data volume on the host: 350 | 351 | id 352 | 353 | Which gives an output like this one: 354 | ```text 355 | uid=1000(myuser) gid=1000(myuser) groups=1000(myuser),4(adm),24(cdrom),27(sudo),46(plugdev),113(lpadmin) 356 | ``` 357 | 358 | The value of `uid` (user ID) and `gid` (group ID) are the ones that you should 359 | be given the container. 360 | 361 | ## Accessing the GUI 362 | 363 | Assuming that container's ports are mapped to the same host's ports, the 364 | interface of the application can be accessed with a web browser at: 365 | 366 | ```text 367 | http://:8181 368 | ``` 369 | 370 | ## Shell Access 371 | 372 | To get shell access to the running container, execute the following command: 373 | 374 | ```shell 375 | docker exec -ti CONTAINER sh 376 | ``` 377 | 378 | Where `CONTAINER` is the ID or the name of the container used during its 379 | creation. 380 | 381 | ## Default Administrator Account 382 | 383 | After a fresh install, use the following credentials to login: 384 | - Email address: `admin@example.com` 385 | - Password: `changeme` 386 | 387 | After you login with this default user, you will be asked to modify your details 388 | and change your password. 389 | 390 | ## Accessibility From The Internet 391 | 392 | **NOTE:** This section assumes that the container is using the default `bridge` 393 | network type. 394 | 395 | For this container to be accessible from the Internet, port forwarding must be 396 | configured on your router. This allows HTTP (port 80) and HTTPs (port 443) 397 | traffic from the Internet to reach this container on your private network. 398 | 399 | Configuration of port forwarding differs from one router to another, but in 400 | general the same information must be configured: 401 | - **External port**: The Internet-side port to be forwarded. 402 | - **Internal port**: The port to forward to. Also called private port. 403 | - **Destination IP address**: The IP address of the device on the local 404 | network to forward to. Also called private IP address. 405 | 406 | The IP address to forward to should be the IP address of the host running the 407 | container. The port to forward to should be the port mapped to the container 408 | during its creation (via the `-p` parameter of the `docker run` command). 409 | 410 | Since the container needs to handle both HTTP and HTTPs traffic, two ports need 411 | to be forwarded: 412 | 413 | | Traffic type | Container port | Host port mapped to container | External port | Internal port | Internal IP address | 414 | |--------------|----------------|-------------------------------|---------------|---------------|-----------------------------------------------| 415 | | HTTP | 8080 | XXXX | 80 | XXXX | IP address of the host running the container. | 416 | | HTTPs | 4443 | YYYY | 443 | YYYY | IP address of the host running the container. | 417 | 418 | `XXXX` and `YYYY` are configurable port values. Unless they conflict with other 419 | used ports on the host, they can simply be set to the same value as the 420 | container port. 421 | 422 | **NOTE**: Some routers don't offer the ability to configure the internal port 423 | to forward to. This means that internal port is the same as the external one. 424 | In a such scenario, `XXXX` must be set to `80` and `YYYY` to `443`. 425 | 426 | For more details about port forwarding, see the following links: 427 | - [How to Port Forward - General Guide to Multiple Router Brands](https://www.noip.com/support/knowledgebase/general-port-forwarding-guide/) 428 | - [How to Forward Ports on Your Router](https://www.howtogeek.com/66214/how-to-forward-ports-on-your-router/) 429 | 430 | ## Troubleshooting 431 | 432 | ### Password Reset 433 | 434 | The password of a user can be reset to `changeme` with the following command: 435 | 436 | ``` 437 | docker exec CONTAINER_NAME /opt/nginx-proxy-manager/bin/reset-password USER_EMAIL 438 | ``` 439 | 440 | Where: 441 | 442 | - `CONTAINER_NAME` is the name of the running container. 443 | - `USER_EMAIL` is the email of the address to reset the password. 444 | 445 | ## Support or Contact 446 | 447 | Having troubles with the container or have questions? Please 448 | [create a new issue]. 449 | 450 | For other great Dockerized applications, see https://jlesage.github.io/docker-apps. 451 | 452 | [create a new issue]: https://github.com/jlesage/docker-nginx-proxy-manager/issues 453 | -------------------------------------------------------------------------------- /appdefs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # 4 | # Definitions for Nginx Proxy Manager docker container. 5 | # 6 | # This file is used as data source to generate README.md and unRAID template files 7 | # from Jinja2 templates. 8 | # 9 | 10 | app: 11 | id: 18 12 | name: nginx-proxy-manager 13 | friendly_name: Nginx Proxy Manager 14 | gui_type: web 15 | base_os: alpine 16 | gui_port: 8181 17 | project: 18 | description: |- 19 | Nginx Proxy Manager enables you to easily forward to your websites running at 20 | home or otherwise, including free SSL, without having to know too much about 21 | Nginx or Letsencrypt. 22 | url: https://nginxproxymanager.com 23 | unraid: 24 | support_url: https://forums.unraid.net/topic/76460-support-nginx-proxy-manager/ 25 | category: "Network:Web Network:Proxy Tools:" 26 | extra_description: >- 27 | **NOTE**: For this container to be accessible from the internet, make sure ports 28 | **80** and **443** on the internet side of your router are properly forwarded to 29 | this container. See the documentation for more details. 30 | 31 | **NOTE**: After a fresh install, the default username/password to connect to the 32 | management interface are: 33 | > admin@example.com/changeme. 34 | documentation: 35 | sections: 36 | - title: Default Administrator Account 37 | level: 2 38 | content: |- 39 | After a fresh install, use the following credentials to login: 40 | - Email address: `admin@example.com` 41 | - Password: `changeme` 42 | 43 | After you login with this default user, you will be asked to modify your details 44 | and change your password. 45 | - title: Accessibility From The Internet 46 | level: 2 47 | content: |- 48 | **NOTE:** This section assumes that the container is using the default `bridge` 49 | network type. 50 | 51 | For this container to be accessible from the Internet, port forwarding must be 52 | configured on your router. This allows HTTP (port 80) and HTTPs (port 443) 53 | traffic from the Internet to reach this container on your private network. 54 | 55 | Configuration of port forwarding differs from one router to another, but in 56 | general the same information must be configured: 57 | - **External port**: The Internet-side port to be forwarded. 58 | - **Internal port**: The port to forward to. Also called private port. 59 | - **Destination IP address**: The IP address of the device on the local 60 | network to forward to. Also called private IP address. 61 | 62 | The IP address to forward to should be the IP address of the host running the 63 | container. The port to forward to should be the port mapped to the container 64 | during its creation (via the `-p` parameter of the `docker run` command). 65 | 66 | Since the container needs to handle both HTTP and HTTPs traffic, two ports need 67 | to be forwarded: 68 | 69 | | Traffic type | Container port | Host port mapped to container | External port | Internal port | Internal IP address | 70 | |--------------|----------------|-------------------------------|---------------|---------------|-----------------------------------------------| 71 | | HTTP | 8080 | XXXX | 80 | XXXX | IP address of the host running the container. | 72 | | HTTPs | 4443 | YYYY | 443 | YYYY | IP address of the host running the container. | 73 | 74 | `XXXX` and `YYYY` are configurable port values. Unless they conflict with other 75 | used ports on the host, they can simply be set to the same value as the 76 | container port. 77 | 78 | **NOTE**: Some routers don't offer the ability to configure the internal port 79 | to forward to. This means that internal port is the same as the external one. 80 | In a such scenario, `XXXX` must be set to `80` and `YYYY` to `443`. 81 | 82 | For more details about port forwarding, see the following links: 83 | - [How to Port Forward - General Guide to Multiple Router Brands](https://www.noip.com/support/knowledgebase/general-port-forwarding-guide/) 84 | - [How to Forward Ports on Your Router](https://www.howtogeek.com/66214/how-to-forward-ports-on-your-router/) 85 | - title: Troubleshooting 86 | level: 2 87 | - title: Password Reset 88 | level: 3 89 | content: |- 90 | The password of a user can be reset to `changeme` with the following command: 91 | 92 | ``` 93 | docker exec CONTAINER_NAME /opt/nginx-proxy-manager/bin/reset-password USER_EMAIL 94 | ``` 95 | 96 | Where: 97 | 98 | - `CONTAINER_NAME` is the name of the running container. 99 | - `USER_EMAIL` is the email of the address to reset the password. 100 | changelog: 101 | - version: 24.07.1 102 | date: 2024-07-05 103 | changes: 104 | - 'Updated Nginx Proxy Manager to version 2.11.3.' 105 | - 'Updated baseimage to version 3.6.2.' 106 | - version: 23.12.2 107 | date: 2023-12-20 108 | changes: 109 | - 'Fixed warning message about uninitialized variable.' 110 | - version: 23.12.1 111 | date: 2023-12-15 112 | changes: 113 | - 'Fixed PowerDNS DNS provider plugin installation.' 114 | - 'Fixed issue where HTTP2 support would always be enabled.' 115 | - 'Fixed server reachability test.' 116 | - 'Updated baseimage to version 3.5.2, which brings the following changes:' 117 | - '2:Mirror for packages installation can be set via the `PACKAGES_MIRROR` environment variable.' 118 | - '2:Improved the way the `take-ownership` script is working.' 119 | - '2:Readiness and minimum running time checks should not be done for a service defined with an interval.' 120 | - '2:Raise an error when a synched service fails to start.' 121 | - '2:Minimum running time check of a service was using an incorrect way to verify if process is still alive.' 122 | - version: 23.08.1 123 | date: 2023-08-04 124 | changes: 125 | - 'Updated Nginx Proxy Manager to version 2.10.4.' 126 | - version: 23.04.1 127 | date: 2023-04-07 128 | changes: 129 | - 'Updated Nginx Proxy Manager to version 2.9.22.' 130 | - version: 23.03.2 131 | date: 2023-03-05 132 | changes: 133 | - 'Fixed compilation parameters that were preventing Nginx from working correctly.' 134 | - version: 23.03.1 135 | date: 2023-03-05 136 | changes: 137 | - 'Updated Nginx Proxy Manager to version 2.9.19.' 138 | - 'Versioning scheme of the Docker image changed to `YY.MM.SEQUENCE`.' 139 | - 'Updated baseimage to version 3.4.6, which brings:' 140 | - '2:Multi-arch image support.' 141 | - '2:Multiple internal functional enhancements and improvements.' 142 | - version: 1.26.1 143 | date: 2022-06-02 144 | changes: 145 | - 'Updated Nginx Proxy Manager to version 2.9.18.' 146 | - version: 1.26.0 147 | date: 2022-02-22 148 | changes: 149 | - 'Updated Nginx Proxy Manager to version 2.9.16.' 150 | - version: 1.25.0 151 | date: 2022-01-21 152 | changes: 153 | - 'Updated Nginx Proxy Manager to version 2.9.15.' 154 | - version: 1.24.1 155 | date: 2022-01-02 156 | changes: 157 | - 'Now using baseimage version 2.4.6, based on Alpine 3.15, which brings the following change:' 158 | - '2:Updated installed packages to get latest security fixes.' 159 | - 'This also fixes an error that was occurring because of the usage of an insufficient Node.js version.' 160 | - version: 1.24.0 161 | date: 2022-01-02 162 | changes: 163 | - 'Updated Nginx Proxy Manager to version 2.9.14.' 164 | - version: 1.23.1 165 | date: 2021-12-27 166 | changes: 167 | - 'Fixed issue where custom SSL certificate could not be added.' 168 | - version: 1.23.0 169 | date: 2021-12-26 170 | changes: 171 | - 'Updated Nginx Proxy Manager to version 2.9.13.' 172 | - 'Updated OpenResty to version 1.19.9.1.' 173 | - 'Fixed rotation of some log files.' 174 | - 'Properly handle nameserver in resolv.conf with interface name appended to its IPv6 address.' 175 | - version: 1.22.1 176 | date: 2021-11-08 177 | changes: 178 | - 'Updated NginxProxyManager to version 2.9.12.' 179 | - version: 1.22.0 180 | date: 2021-11-02 181 | changes: 182 | - 'Updated Nginx Proxy Manager to version 2.9.11.' 183 | - version: 1.21.1 184 | date: 2021-09-21 185 | changes: 186 | - 'Fixed issue where configuration of a proxy host could fail to load.' 187 | - version: 1.21.0 188 | date: 2021-09-11 189 | changes: 190 | - 'Updated Nginx Proxy Manager to version 2.9.9.' 191 | - version: 1.20.0 192 | date: 2021-08-28 193 | changes: 194 | - 'Updated Nginx Proxy Manager to version 2.9.8.' 195 | - version: 1.19.0 196 | date: 2021-08-08 197 | changes: 198 | - 'Updated Nginx Proxy Manager to version 2.9.7.' 199 | - version: 1.18.0 200 | date: 2021-08-03 201 | changes: 202 | - 'Updated Nginx Proxy Manager to version 2.6.9.' 203 | - 'Removed random delay that was applied when manually renewing a certificate.' 204 | - version: 1.17.0 205 | date: 2021-06-25 206 | changes: 207 | - 'Updated Nginx Proxy Manager to version 2.9.4.' 208 | - version: 1.16.1 209 | date: 2021-05-31 210 | changes: 211 | - 'Fixed issue where Certbot plugins installation would cause Python packages conflicts.' 212 | - version: 1.16.0 213 | date: 2021-05-21 214 | changes: 215 | - 'Updated Nginx Proxy Manager to version 2.9.3.' 216 | - version: 1.15.0 217 | date: 2021-05-10 218 | changes: 219 | - 'Updated Nginx Proxy Manager to version 2.9.2.' 220 | - 'Use the most recent certbot version.' 221 | - 'Updated baseimage to version 2.4.5.' 222 | - version: 1.14.0 223 | date: 2021-03-22 224 | changes: 225 | - 'Updated Nginx Proxy Manager to version 2.8.1.' 226 | - version: 1.13.1 227 | date: 2021-03-13 228 | changes: 229 | - 'Fixed issue where saving the default site setting would fail.' 230 | - version: 1.13.0 231 | date: 2021-02-09 232 | changes: 233 | - 'Updated Nginx Proxy Manager to version 2.8.0.' 234 | - 'Updated OpenResty to version 1.19.3.1.' 235 | - 'Replaced the depricated GeoIP module by GeoIP2.' 236 | - version: 1.12.0 237 | date: 2021-01-06 238 | changes: 239 | - 'Updated Nginx Proxy Manager to version 2.7.2.' 240 | - 'Added the GeoIP Nginx module.' 241 | - version: 1.11.0 242 | date: 2020-11-24 243 | changes: 244 | - 'Updated Nginx Proxy Manager to version 2.7.1.' 245 | - 'Fixed rotation of nginx log files.' 246 | - version: 1.10.3 247 | date: 2020-11-06 248 | changes: 249 | - 'Updated Nginx Proxy Manager to version 2.6.2.' 250 | - version: 1.10.2 251 | date: 2020-10-26 252 | changes: 253 | - 'Fixed generation of certificates that use DNS challenge.' 254 | - version: 1.10.1 255 | date: 2020-10-26 256 | changes: 257 | - 'Fixed automatic installation of Certbot plugins.' 258 | - version: 1.10.0 259 | date: 2020-10-25 260 | changes: 261 | - 'Updated Nginx Proxy Manager to version 2.6.1.' 262 | - 'Now using baseimage version 2.4.4, based on Alpine 3.12, which brings the following changes:' 263 | - '2:Upgraded glibc to version 2.31 on Alpine Linux images with glibc integrated.' 264 | - '2:Updated installed packages to get latest security fixes.' 265 | - 'Use sqlite as database instead of mariadb.' 266 | - 'Added proper support for the `DISABLE_IPV6` environment variable.' 267 | - 'Periodically clean Let''s Encrypt certificates.' 268 | - version: 1.9.2 269 | date: 2020-07-10 270 | changes: 271 | - 'Fixed an issue where the container would fail to start if custom Nginx files are mounted by Docker.' 272 | - version: 1.9.1 273 | date: 2020-07-10 274 | changes: 275 | - 'Improved compatibility by disabling usage of SSE4.2 in OpenResty.' 276 | - version: 1.9.0 277 | date: 2020-07-09 278 | changes: 279 | - 'Upgraded Nginx Proxy Manager to version 2.3.1.' 280 | - version: 1.8.1 281 | date: 2020-05-21 282 | changes: 283 | - 'Upgraded Nginx Proxy Manager to version 2.2.4.' 284 | - version: 1.8.0 285 | date: 2020-04-15 286 | changes: 287 | - 'Upgraded Nginx Proxy Manager to version 2.2.3.' 288 | - version: 1.7.4 289 | date: 2020-04-08 290 | changes: 291 | - 'Fixed issue where generated proxy host config would use incorrect ports.' 292 | - version: 1.7.3 293 | date: 2020-04-07 294 | changes: 295 | - 'Upgraded Nginx Proxy Manager to version 2.2.2.' 296 | - version: 1.7.2 297 | date: 2020-04-06 298 | changes: 299 | - 'Fixed generation of resolvers.conf when IPv6 is involved.' 300 | - version: 1.7.1 301 | date: 2020-04-06 302 | changes: 303 | - 'Upgraded Nginx Proxy Manager to version 2.2.1.' 304 | - version: 1.7.0 305 | date: 2020-03-16 306 | changes: 307 | - 'Upgraded Nginx Proxy Manager to version 2.2.0.' 308 | - 'Now using baseimage v2.4.3, which brings the following changes:' 309 | - '2:Updated installed packages to get latest security fixes.' 310 | - '2:Make sure the tzdata is installed.' 311 | - version: 1.6.0 312 | date: 2019-10-10 313 | changes: 314 | - 'Rotate Nginx log files.' 315 | - 'Added tool to reset password of a user of the management interface.' 316 | - version: 1.5.3 317 | date: 2019-09-04 318 | changes: 319 | - 'Upgraded Nginx Proxy Manager to version 2.0.14.' 320 | - version: 1.5.2 321 | date: 2019-08-20 322 | changes: 323 | - 'Use baseimage based on Alpine 3.9.' 324 | - '2:This fixes TLS1.3 support.' 325 | - 'Keep the `fastcgi.conf` and `fastcgi_params` Nginx configuration files.' 326 | - version: 1.5.1 327 | date: 2019-07-02 328 | changes: 329 | - 'Fixed issue where the wrong SSL protocols/ciphers were used.' 330 | - version: 1.5.0 331 | date: 2019-05-09 332 | changes: 333 | - 'Upgraded Nginx Proxy Manager to version 2.0.13.' 334 | - 'Now using baseimage v2.4.2, which brings the following changes:' 335 | - '2:Updated installed packages to get latest security fixes.' 336 | - version: 1.4.2 337 | date: 2019-04-18 338 | changes: 339 | - 'Fixed issue where using a hostname for the upstream proxy host would not work.' 340 | - version: 1.4.1 341 | date: 2019-04-10 342 | changes: 343 | - 'Updated Nginx Proxy Manager to version 2.0.12.' 344 | - 'Fixed a directory traversal vulnerability.' 345 | - version: 1.4.0 346 | date: 2019-03-05 347 | changes: 348 | - 'Upgraded Nginx Proxy Manager version 2.0.11.' 349 | - version: 1.3.1 350 | date: 2019-02-21 351 | changes: 352 | - 'During startup, make sure there is no migration lock held.' 353 | - version: 1.3.0 354 | date: 2019-02-21 355 | changes: 356 | - 'Make sure to upgrade the database if required.' 357 | - 'Properly notify the supervisor when the database is ready.' 358 | - 'Make sure to not use the database directory if it was not initialized successfully.' 359 | - 'Upgraded Nginx Proxy Manager to version 2.0.9.' 360 | - '2:Fixed issue where HTTP/2 option could not be disabled.' 361 | - '2:Added HSTS settings.' 362 | - version: 1.2.1 363 | date: 2019-01-26 364 | changes: 365 | - 'Upgraded Nginx Proxy Manager to version 2.0.9.' 366 | - '2:Increased custom SSL certificate file size limit.' 367 | - version: 1.2.0 368 | date: 2019-01-07 369 | changes: 370 | - 'Upgraded Nginx Proxy Manager to version 2.0.8.' 371 | - '2:Added the ability to enable/disable hosts.' 372 | - '2:IP ranges are now fetched dynamically.' 373 | - version: 1.1.0 374 | date: 2019-01-03 375 | changes: 376 | - 'Upgraded Nginx Proxy Manager to version 2.0.7.' 377 | - '2:Added HTTP/2 option for SSL enabled hosts.' 378 | - '2:Added upstream SSL option for proxy hosts.' 379 | - version: 1.0.1 380 | date: 2018-12-19 381 | changes: 382 | - 'Fixed an issue where creation of an access list would fail.' 383 | - version: 1.0.0 384 | date: 2018-12-19 385 | changes: 386 | - 'Initial release.' 387 | 388 | container: 389 | unsupported_volume: /storage 390 | 391 | # Environment variables. 392 | environment_variables: 393 | - name: DISABLE_IPV6 394 | description: >- 395 | When set to `1`, IPv6 support is disabled. This is needed when IPv6 is 396 | not enabled/supported on the host. 397 | type: public 398 | default: 0 399 | unraid_template: 400 | title: Disable IPv6 401 | description: >- 402 | When set to '1', IPv6 support is disabled. 403 | display: advanced 404 | required: false 405 | mask: false 406 | 407 | # Volumes 408 | volumes: [] 409 | 410 | # Network ports 411 | ports: 412 | - number: 8181 413 | protocol: tcp 414 | description: >- 415 | Port used to access the web interface of the application. 416 | mandatory: true 417 | include_in_quick_start: true 418 | unraid_template: 419 | title: Web UI Port 420 | description: >- 421 | Port used to access the web interface of the application. 422 | default: "{{ 7800 + app.id|int }}" 423 | display: always 424 | required: true 425 | mask: false 426 | - number: 8080 427 | protocol: tcp 428 | description: >- 429 | Port used to serve HTTP requests. 430 | mandatory: true 431 | include_in_quick_start: true 432 | unraid_template: 433 | title: HTTP Port 434 | description: >- 435 | Port used to serve HTTP requests. NOTE: Your router should be 436 | configured to forward port 80 to this port. If your router doesn't 437 | allow setting the destination/internal IP address, this *must* be set 438 | to 80. 439 | default: "{{ app.id|int }}80" 440 | display: always 441 | required: true 442 | mask: false 443 | - number: 4443 444 | protocol: tcp 445 | description: >- 446 | Port used to serve HTTPs requests. 447 | mandatory: true 448 | include_in_quick_start: true 449 | unraid_template: 450 | title: HTTPs Port 451 | description: >- 452 | Port used to serve HTTPs requests. NOTE: Your router should be 453 | configured to forward port 443 to this port. If your router doesn't 454 | allow setting the destination/internal IP address, this *must* be set 455 | to 443. 456 | default: "{{ app.id|int }}443" 457 | display: always 458 | required: true 459 | mask: false 460 | 461 | # Devices 462 | devices: [] 463 | -------------------------------------------------------------------------------- /rootfs/defaults/production.json: -------------------------------------------------------------------------------- 1 | { 2 | "database": { 3 | "engine": "knex-native", 4 | "knex": { 5 | "client": "sqlite3", 6 | "connection": { 7 | "filename": "/config/database.sqlite" 8 | }, 9 | "useNullAsDefault": true 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /rootfs/etc/cont-init.d/54-db-upgrade.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e # Exit immediately if a command exits with a non-zero status. 4 | set -u # Treat unset variables as an error. 5 | 6 | log() { 7 | if [ -n "${1-}" ]; then 8 | echo "$@" 9 | else 10 | while read OUTPUT; do 11 | echo "$OUTPUT" 12 | done 13 | fi 14 | } 15 | 16 | check_db() { 17 | echo 'SELECT 1' | mysql &> /dev/null 18 | } 19 | 20 | install_db() { 21 | log "Installing database..." 22 | add-pkg --virtual upgrade-deps mariadb mariadb-client jq | log 23 | # Make sure mariadb listen on port 3306 24 | #sed-patch 's/^skip-networking/#skip-networking/' /etc/my.cnf.d/mariadb-server.cnf 25 | } 26 | 27 | uninstall_db() { 28 | log "Uninstalling database..." 29 | del-pkg upgrade-deps | log 30 | } 31 | 32 | start_db() { 33 | log "Starting database..." 34 | 35 | # Start mysqld. 36 | mkdir -p /run/mysqld 37 | /usr/bin/mysqld --user=$(whoami) --datadir /config/mysql --tmpdir /tmp/ & 38 | pid="$!" 39 | 40 | # Wait until it is ready. 41 | for i in $(seq 1 30); do 42 | if check_db; then 43 | break 44 | fi 45 | sleep 1 46 | done 47 | 48 | if ! check_db; then 49 | log "ERROR: Failed to start the database." 50 | exit 1 51 | fi 52 | } 53 | 54 | stop_db() { 55 | # Kill mysqld. 56 | log "Shutting down database..." 57 | if ! kill -s TERM "$pid" || ! wait "$pid"; then 58 | log "ERROR: Failed to stop the database." 59 | exit 1 60 | fi 61 | } 62 | 63 | # Check if a mysql database exists. 64 | if [ ! -d /config/mysql ]; then 65 | exit 0 66 | fi 67 | 68 | # Handle case where a previous conversion didn't went well. 69 | if [ -f /config/db_convert_in_progress ]; then 70 | rm -f /config/database.sqlite 71 | fi 72 | 73 | log "MySQL database conversion needed." 74 | touch /config/db_convert_in_progress 75 | 76 | # Temporarily start the database. 77 | install_db 78 | start_db 79 | 80 | # Dump the database. 81 | log "Dumping database..." 82 | /usr/bin/mysqldump --skip-extended-insert --compact nginxproxymanager > /tmp/mysqldump.sql 83 | 84 | # Convert the database. 85 | log "Converting database..." 86 | /opt/nginx-proxy-manager/bin/mysql2sqlite /tmp/mysqldump.sql | sqlite3 /config/database.sqlite 87 | 88 | # Update the database settings in configuration. 89 | if [ -f /config/production.json ]; then 90 | if [ "$(jq -r '.database.engine' /config/production.json)" == "mysql" ] 91 | then 92 | log "Updating database settings in config file..." 93 | jq -n 'input | .database = input.database' /config/production.json /defaults/production.json > /config/production.json.updated 94 | fi 95 | fi 96 | 97 | # Database converted properly. 98 | log "Database conversion done." 99 | rm /config/db_convert_in_progress 100 | rm /tmp/mysqldump.sql 101 | mv /config/mysql /config/mysql.converted 102 | if [ -f /config/production.json.updated ]; then 103 | mv /config/production.json /config/production.json.old 104 | mv /config/production.json.updated /config/production.json 105 | fi 106 | 107 | # Stop the database. 108 | stop_db 109 | uninstall_db 110 | 111 | # vim:ft=sh:ts=4:sw=4:et:sts=4 112 | -------------------------------------------------------------------------------- /rootfs/etc/cont-init.d/55-nginx-proxy-manager.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e # Exit immediately if a command exits with a non-zero status. 4 | set -u # Treat unset variables as an error. 5 | 6 | log() { 7 | echo "$@" 8 | } 9 | 10 | # Make sure mandatory directories exist. 11 | mkdir -p \ 12 | /config/log \ 13 | /config/letsencrypt/archive \ 14 | /config/letsencrypt-acme-challenge \ 15 | /config/custom_ssl \ 16 | /config/access \ 17 | /config/nginx/default_host \ 18 | /config/nginx/default_www \ 19 | /config/nginx/cache \ 20 | /config/nginx/proxy_host \ 21 | /config/nginx/redirection_host \ 22 | /config/nginx/stream \ 23 | /config/nginx/dead_host \ 24 | /config/nginx/temp \ 25 | /config/log/letsencrypt \ 26 | /config/letsencrypt-workdir \ 27 | 28 | # Make sure directories required for nginx exist. 29 | for DIR in /var/run/nginx /var/tmp/nginx 30 | do 31 | mkdir -p "$DIR" 32 | chown app:app "$DIR" 33 | done 34 | 35 | # Create symlinks for logs. 36 | [ ! -L /config/log/log ] || rm /config/log/log 37 | ln -snf log /config/logs 38 | 39 | # Make sure to remove old letsencrypt config file. 40 | [ ! -f $XDG_CONFIG_HOME/letsencrypt/cli.ini ] || mv $XDG_CONFIG_HOME/letsencrypt/cli.ini $XDG_CONFIG_HOME/letsencrypt/cli.ini.removed 41 | 42 | # Fix any references to the old log path. 43 | find /config/nginx -not \( -path /config/nginx/custom -prune \) -type f -name '*.conf' | while read file 44 | do 45 | sed -i 's|/data/logs/|/config/log/|' "$file" 46 | done 47 | 48 | # Install default config. 49 | [ -f /config/nginx/ip_ranges.conf ] || cp /defaults/ip_ranges.conf /config/nginx/ 50 | [ -f /config/production.json ] || cp /defaults/production.json /config/ 51 | 52 | # Make sure there is no migration lock held. 53 | # See https://github.com/jlesage/docker-nginx-proxy-manager/issues/4 54 | if [ -f /config/database.sqlite ]; then 55 | echo 'DELETE FROM migrations_lock WHERE is_locked = 1;' | sqlite3 /config/database.sqlite 56 | fi 57 | 58 | # Generate the resolvers configuration file. 59 | if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; 60 | then 61 | echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) ipv6=off valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf 62 | else 63 | echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf 64 | fi 65 | 66 | # Handle IPv6 settings. 67 | /opt/nginx-proxy-manager/bin/handle-ipv6-setting /etc/nginx/conf.d 68 | /opt/nginx-proxy-manager/bin/handle-ipv6-setting /config/nginx 69 | 70 | # vim:ft=sh:ts=4:sw=4:et:sts=4 71 | -------------------------------------------------------------------------------- /rootfs/etc/services.d/app/nginx.dep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LePresidente/docker-nginx-proxy-manager/5ab02e9052b0a2c98348635f564591c09fbd3083/rootfs/etc/services.d/app/nginx.dep -------------------------------------------------------------------------------- /rootfs/etc/services.d/cert_cleanup/interval: -------------------------------------------------------------------------------- 1 | 604800 2 | -------------------------------------------------------------------------------- /rootfs/etc/services.d/cert_cleanup/run: -------------------------------------------------------------------------------- 1 | /opt/nginx-proxy-manager/bin/lecleaner -------------------------------------------------------------------------------- /rootfs/etc/services.d/default/cert_cleanup.dep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LePresidente/docker-nginx-proxy-manager/5ab02e9052b0a2c98348635f564591c09fbd3083/rootfs/etc/services.d/default/cert_cleanup.dep -------------------------------------------------------------------------------- /rootfs/etc/services.d/nginx/respawn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LePresidente/docker-nginx-proxy-manager/5ab02e9052b0a2c98348635f564591c09fbd3083/rootfs/etc/services.d/nginx/respawn -------------------------------------------------------------------------------- /rootfs/etc/services.d/nginx/run: -------------------------------------------------------------------------------- 1 | /usr/sbin/nginx -------------------------------------------------------------------------------- /rootfs/opt/nginx-proxy-manager/bin/handle-ipv6-setting: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This command reads the `DISABLE_IPV6` env var and will either enable 4 | # or disable ipv6 in all nginx configs based on this setting. 5 | 6 | # Lowercase 7 | DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]') 8 | 9 | FOLDER=$1 10 | if [ "$FOLDER" == "" ]; then 11 | echo "$0 requires a absolute folder path as the first argument!" 12 | exit 1 13 | fi 14 | 15 | FILES=$(find "$FOLDER" -type f -name "*.conf") 16 | SED_REGEX= 17 | 18 | if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then 19 | # IPV6 is disabled 20 | echo "Disabling IPV6 in hosts in: $1" 21 | SED_REGEX='s/^([^#]*)listen \[::\]/\1#listen [::]/g' 22 | else 23 | # IPV6 is enabled 24 | echo "Enabling IPV6 in hosts in: $1" 25 | SED_REGEX='s/^(\s*)#listen \[::\]/\1listen [::]/g' 26 | fi 27 | 28 | for FILE in $FILES 29 | do 30 | echo "- ${FILE}" 31 | echo "$(sed -E "$SED_REGEX" "$FILE")" > $FILE 32 | done 33 | -------------------------------------------------------------------------------- /rootfs/opt/nginx-proxy-manager/bin/lecleaner: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import os.path 4 | import os 5 | 6 | from datetime import datetime 7 | from pathlib import Path 8 | 9 | BASE = "/etc/letsencrypt/" 10 | 11 | live_dir = os.path.join(BASE, "live") 12 | archive_dir = os.path.join(BASE, "archive") 13 | csr_dir = os.path.join(BASE, "csr") 14 | key_dir = os.path.join(BASE, "keys") 15 | 16 | # Set of certificate paths actively used. 17 | in_use = set() 18 | 19 | keep_count = 0 20 | delete_count = 0 21 | error_count = 0 22 | 23 | def remove_file(f): 24 | success = False 25 | try: 26 | os.remove(f) 27 | success = True 28 | except OSError as e: 29 | print("ERROR: Could not remove {}: {}.".format(f, e)) 30 | return success 31 | 32 | # Build the set of certificates in use. 33 | for domain in os.path.isdir(live_dir) and os.listdir(live_dir) or [] : 34 | domain_dir = os.path.join(live_dir, domain) 35 | if not os.path.isdir(domain_dir): 36 | continue 37 | for certlink in os.listdir(domain_dir): 38 | f = os.path.join(domain_dir, certlink) 39 | if not os.path.islink(f): 40 | continue 41 | target = Path(f).resolve() 42 | in_use.add(target) 43 | 44 | print("----------------------------------------------------------") 45 | print("Let's Encrypt certificates cleanup - {}".format(datetime.now().strftime("%Y/%m/%d %H:%M:%S"))) 46 | print("----------------------------------------------------------") 47 | 48 | # Remove all unused certificates from the archive directory. 49 | for domain in os.path.isdir(archive_dir) and os.listdir(archive_dir) or []: 50 | domain_dir = os.path.join(archive_dir, domain) 51 | if not os.path.isdir(domain_dir): 52 | continue 53 | for certfile in os.listdir(domain_dir): 54 | f = Path(os.path.join(domain_dir, certfile)) 55 | if f.resolve() in in_use: 56 | print("Keeping {}.".format(f)) 57 | keep_count += 1 58 | else: 59 | print("Deleting {}.".format(f)) 60 | if remove_file(f): 61 | delete_count += 1 62 | else: 63 | error_count += 1 64 | 65 | # Remove all files from the csr and key directories. 66 | for dir in [ csr_dir, key_dir ]: 67 | for file in os.path.isdir(dir) and os.listdir(dir) or []: 68 | f = os.path.join(dir, file) 69 | if not os.path.isfile(f): 70 | continue 71 | print("Deleting {}.".format(f)) 72 | if remove_file(f): 73 | delete_count += 1 74 | else: 75 | error_count += 1 76 | 77 | print("{} file(s) kept.".format(keep_count)) 78 | print("{} file(s) deleted.".format(delete_count)) 79 | if error_count > 0: 80 | print("{} file(s) failed to be deleted.".format(error_count)) 81 | -------------------------------------------------------------------------------- /rootfs/opt/nginx-proxy-manager/bin/mysql2sqlite: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | # Authors: @esperlu, @artemyk, @gkuenning, @dumblob 4 | 5 | # FIXME detect empty input file and issue a warning 6 | 7 | function printerr( s ){ print s | "cat >&2" } 8 | 9 | BEGIN { 10 | if( ARGC != 2 ){ 11 | printerr( \ 12 | "USAGE: mysql2sqlite dump_mysql.sql > dump_sqlite3.sql\n" \ 13 | " file name - (dash) is not supported, because - means stdin") 14 | no_END = 1 15 | exit 1 16 | } 17 | 18 | # Find INT_MAX supported by both this AWK (usually an ISO C signed int) 19 | # and SQlite. 20 | # On non-8bit-based architectures, the additional bits are safely ignored. 21 | 22 | # 8bit (lower precision should not exist) 23 | s="127" 24 | # "63" + 0 avoids potential parser misbehavior 25 | if( (s + 0) "" == s ){ INT_MAX_HALF = "63" + 0 } 26 | # 16bit 27 | s="32767" 28 | if( (s + 0) "" == s ){ INT_MAX_HALF = "16383" + 0 } 29 | # 32bit 30 | s="2147483647" 31 | if( (s + 0) "" == s ){ INT_MAX_HALF = "1073741823" + 0 } 32 | # 64bit (as INTEGER in SQlite3) 33 | s="9223372036854775807" 34 | if( (s + 0) "" == s ){ INT_MAX_HALF = "4611686018427387904" + 0 } 35 | # # 128bit 36 | # s="170141183460469231731687303715884105728" 37 | # if( (s + 0) "" == s ){ INT_MAX_HALF = "85070591730234615865843651857942052864" + 0 } 38 | # # 256bit 39 | # s="57896044618658097711785492504343953926634992332820282019728792003956564819968" 40 | # if( (s + 0) "" == s ){ INT_MAX_HALF = "28948022309329048855892746252171976963317496166410141009864396001978282409984" + 0 } 41 | # # 512bit 42 | # s="6703903964971298549787012499102923063739682910296196688861780721860882015036773488400937149083451713845015929093243025426876941405973284973216824503042048" 43 | # if( (s + 0) "" == s ){ INT_MAX_HALF = "3351951982485649274893506249551461531869841455148098344430890360930441007518386744200468574541725856922507964546621512713438470702986642486608412251521024" + 0 } 44 | # # 1024bit 45 | # s="89884656743115795386465259539451236680898848947115328636715040578866337902750481566354238661203768010560056939935696678829394884407208311246423715319737062188883946712432742638151109800623047059726541476042502884419075341171231440736956555270413618581675255342293149119973622969239858152417678164812112068608" 46 | # if( (s + 0) "" == s ){ INT_MAX_HALF = "44942328371557897693232629769725618340449424473557664318357520289433168951375240783177119330601884005280028469967848339414697442203604155623211857659868531094441973356216371319075554900311523529863270738021251442209537670585615720368478277635206809290837627671146574559986811484619929076208839082406056034304" + 0 } 47 | # # higher precision probably not needed 48 | 49 | FS=",$" 50 | print "PRAGMA synchronous = OFF;" 51 | print "PRAGMA journal_mode = MEMORY;" 52 | print "BEGIN TRANSACTION;" 53 | } 54 | 55 | # historically 3 spaces separate non-argument local variables 56 | function bit_to_int( str_bit, powtwo, i, res, bit, overflow ){ 57 | powtwo = 1 58 | overflow = 0 59 | # 011101 = 1*2^0 + 0*2^1 + 1*2^2 ... 60 | for( i = length( str_bit ); i > 0; --i ){ 61 | bit = substr( str_bit, i, 1 ) 62 | if( overflow || ( bit == 1 && res > INT_MAX_HALF ) ){ 63 | printerr( \ 64 | NR ": WARN Bit field overflow, number truncated (LSBs saved, MSBs ignored)." ) 65 | break 66 | } 67 | res = res + bit * powtwo 68 | # no warning here as it might be the last iteration 69 | if( powtwo > INT_MAX_HALF ){ overflow = 1; continue } 70 | powtwo = powtwo * 2 71 | } 72 | return res 73 | } 74 | 75 | # CREATE TRIGGER statements have funny commenting. Remember we are in trigger. 76 | /^\/\*.*(CREATE.*TRIGGER|create.*trigger)/ { 77 | gsub( /^.*(TRIGGER|trigger)/, "CREATE TRIGGER" ) 78 | print 79 | inTrigger = 1 80 | next 81 | } 82 | # The end of CREATE TRIGGER has a stray comment terminator 83 | /(END|end) \*\/;;/ { gsub( /\*\//, "" ); print; inTrigger = 0; next } 84 | # The rest of triggers just get passed through 85 | inTrigger != 0 { print; next } 86 | 87 | # CREATE VIEW looks like a TABLE in comments 88 | /^\/\*.*(CREATE.*TABLE|create.*table)/ { 89 | inView = 1 90 | next 91 | } 92 | # end of CREATE VIEW 93 | /^(\).*(ENGINE|engine).*\*\/;)/ { 94 | inView = 0 95 | next 96 | } 97 | # content of CREATE VIEW 98 | inView != 0 { next } 99 | 100 | # skip comments 101 | /^\/\*/ { next } 102 | 103 | # skip PARTITION statements 104 | /^ *[(]?(PARTITION|partition) +[^ ]+/ { next } 105 | 106 | # print all INSERT lines 107 | ( /^ *\(/ && /\) *[,;] *$/ ) || /^(INSERT|insert|REPLACE|replace)/ { 108 | prev = "" 109 | 110 | # first replace \\ by \_ that mysqldump never generates to deal with 111 | # sequnces like \\n that should be translated into \n, not \. 112 | # After we convert all escapes we replace \_ by backslashes. 113 | gsub( /\\\\/, "\\_" ) 114 | 115 | # single quotes are escaped by another single quote 116 | gsub( /\\'/, "''" ) 117 | gsub( /\\n/, "\n" ) 118 | gsub( /\\r/, "\r" ) 119 | gsub( /\\"/, "\"" ) 120 | gsub( /\\\032/, "\032" ) # substitute char 121 | 122 | gsub( /\\_/, "\\" ) 123 | 124 | # sqlite3 is limited to 16 significant digits of precision 125 | while( match( $0, /0x[0-9a-fA-F]{17}/ ) ){ 126 | hexIssue = 1 127 | sub( /0x[0-9a-fA-F]+/, substr( $0, RSTART, RLENGTH-1 ), $0 ) 128 | } 129 | if( hexIssue ){ 130 | printerr( \ 131 | NR ": WARN Hex number trimmed (length longer than 16 chars)." ) 132 | hexIssue = 0 133 | } 134 | print 135 | next 136 | } 137 | 138 | # CREATE DATABASE is not supported 139 | /^(CREATE.*DATABASE|create.*database)/ { next } 140 | 141 | # print the CREATE line as is and capture the table name 142 | /^(CREATE|create)/ { 143 | if( $0 ~ /IF NOT EXISTS|if not exists/ || $0 ~ /TEMPORARY|temporary/ ){ 144 | caseIssue = 1 145 | printerr( \ 146 | NR ": WARN Potential case sensitivity issues with table/column naming\n" \ 147 | " (see INFO at the end)." ) 148 | } 149 | if( match( $0, /`[^`]+/ ) ){ 150 | tableName = substr( $0, RSTART+1, RLENGTH-1 ) 151 | } 152 | aInc = 0 153 | prev = "" 154 | firstInTable = 1 155 | print 156 | next 157 | } 158 | 159 | # Replace `FULLTEXT KEY` (probably other `XXXXX KEY`) 160 | /^ (FULLTEXT KEY|fulltext key)/ { gsub( /[A-Za-z ]+(KEY|key)/, " KEY" ) } 161 | 162 | # Get rid of field lengths in KEY lines 163 | / (PRIMARY |primary )?(KEY|key)/ { gsub( /\([0-9]+\)/, "" ) } 164 | 165 | aInc == 1 && /PRIMARY KEY|primary key/ { next } 166 | 167 | # Replace COLLATE xxx_xxxx_xx statements with COLLATE BINARY 168 | / (COLLATE|collate) [a-z0-9_]*/ { gsub( /(COLLATE|collate) [a-z0-9_]*/, "COLLATE BINARY" ) } 169 | 170 | # Print all fields definition lines except the `KEY` lines. 171 | /^ / && !/^( (KEY|key)|\);)/ { 172 | if( match( $0, /[^"`]AUTO_INCREMENT|auto_increment[^"`]/) ){ 173 | aInc = 1 174 | gsub( /AUTO_INCREMENT|auto_increment/, "PRIMARY KEY AUTOINCREMENT" ) 175 | } 176 | gsub( /(UNIQUE KEY|unique key) (`.*`|".*") /, "UNIQUE " ) 177 | gsub( /(CHARACTER SET|character set) [^ ]+[ ,]/, "" ) 178 | # FIXME 179 | # CREATE TRIGGER [UpdateLastTime] 180 | # AFTER UPDATE 181 | # ON Package 182 | # FOR EACH ROW 183 | # BEGIN 184 | # UPDATE Package SET LastUpdate = CURRENT_TIMESTAMP WHERE ActionId = old.ActionId; 185 | # END 186 | gsub( /(ON|on) (UPDATE|update) (CURRENT_TIMESTAMP|current_timestamp)(\(\))?/, "" ) 187 | gsub( /(DEFAULT|default) (CURRENT_TIMESTAMP|current_timestamp)(\(\))?/, "DEFAULT current_timestamp") 188 | gsub( /(COLLATE|collate) [^ ]+ /, "" ) 189 | gsub( /(ENUM|enum)[^)]+\)/, "text " ) 190 | gsub( /(SET|set)\([^)]+\)/, "text " ) 191 | gsub( /UNSIGNED|unsigned/, "" ) 192 | gsub( /_utf8mb3/, "" ) 193 | gsub( /` [^ ]*(INT|int|BIT|bit)[^ ]*/, "` integer" ) 194 | gsub( /" [^ ]*(INT|int|BIT|bit)[^ ]*/, "\" integer" ) 195 | ere_bit_field = "[bB]'[10]+'" 196 | if( match($0, ere_bit_field) ){ 197 | sub( ere_bit_field, bit_to_int( substr( $0, RSTART +2, RLENGTH -2 -1 ) ) ) 198 | } 199 | 200 | # remove USING BTREE and other suffixes for USING, for example: "UNIQUE KEY 201 | # `hostname_domain` (`hostname`,`domain`) USING BTREE," 202 | gsub( / USING [^, ]+/, "" ) 203 | 204 | # field comments are not supported 205 | gsub( / (COMMENT|comment).+$/, "" ) 206 | # Get commas off end of line 207 | gsub( /,.?$/, "" ) 208 | if( prev ){ 209 | if( firstInTable ){ 210 | print prev 211 | firstInTable = 0 212 | } 213 | else { 214 | print "," prev 215 | } 216 | } 217 | else { 218 | # FIXME check if this is correct in all cases 219 | if( match( $1, 220 | /(CONSTRAINT|constraint) ["].*["] (FOREIGN KEY|foreign key)/ ) ){ 221 | print "," 222 | } 223 | } 224 | prev = $1 225 | } 226 | 227 | / ENGINE| engine/ { 228 | if( prev ){ 229 | if( firstInTable ){ 230 | print prev 231 | firstInTable = 0 232 | } 233 | else { 234 | print "," prev 235 | } 236 | } 237 | prev="" 238 | print ");" 239 | next 240 | } 241 | # `KEY` lines are extracted from the `CREATE` block and stored in array for later print 242 | # in a separate `CREATE KEY` command. The index name is prefixed by the table name to 243 | # avoid a sqlite error for duplicate index name. 244 | /^( (KEY|key)|\);)/ { 245 | if( prev ){ 246 | if( firstInTable ){ 247 | print prev 248 | firstInTable = 0 249 | } 250 | else { 251 | print "," prev 252 | } 253 | } 254 | prev = "" 255 | if( $0 == ");" ){ 256 | print 257 | } 258 | else { 259 | if( match( $0, /`[^`]+/ ) ){ 260 | indexName = substr( $0, RSTART+1, RLENGTH-1 ) 261 | } 262 | if( match( $0, /\([^()]+/ ) ){ 263 | indexKey = substr( $0, RSTART+1, RLENGTH-1 ) 264 | } 265 | # idx_ prefix to avoid name clashes (they really happen!) 266 | key[tableName] = key[tableName] "CREATE INDEX \"idx_" \ 267 | tableName "_" indexName "\" ON \"" tableName "\" (" indexKey ");\n" 268 | } 269 | } 270 | 271 | END { 272 | if( no_END ){ exit 1} 273 | # print all KEY creation lines. 274 | for( table in key ){ printf key[table] } 275 | 276 | print "END TRANSACTION;" 277 | 278 | if( caseIssue ){ 279 | printerr( \ 280 | "INFO Pure sqlite identifiers are case insensitive (even if quoted\n" \ 281 | " or if ASCII) and doesnt cross-check TABLE and TEMPORARY TABLE\n" \ 282 | " identifiers. Thus expect errors like \"table T has no column named F\".") 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /rootfs/opt/nginx-proxy-manager/bin/reset-password: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | usage() { 4 | echo "usage: $(basename "$0") USER_EMAIL [PASSWORD] 5 | 6 | Reset password of a Nginx Proxy Manager user. 7 | 8 | Arguments: 9 | USER_EMAIL Email address of the user to reset the password. 10 | PASSWORD Optional new password of the user. If not set, password 11 | is set to 'changeme'. 12 | " 13 | exit 1 14 | } 15 | 16 | USER_EMAIL="$1" 17 | if [ -z "$USER_EMAIL" ]; then 18 | echo "ERROR: User email address must be set." 19 | usage 20 | fi 21 | PASSWORD_HASH="$(/usr/bin/bcrypt-tool hash "${2:-changeme}" 13)" 22 | 23 | /usr/bin/sqlite3 /config/database.sqlite "UPDATE auth SET secret = '$PASSWORD_HASH' WHERE EXISTS (SELECT * FROM user WHERE user.id = auth.user_id AND user.email = '$USER_EMAIL')" 24 | -------------------------------------------------------------------------------- /rootfs/startapp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e # Exit immediately if a command exits with a non-zero status. 4 | set -u # Treat unset variables as an error. 5 | 6 | export HOME=/config 7 | export NODE_ENV=production 8 | export SUPPRESS_NO_CONFIG_WARNING=1 9 | 10 | cd /opt/nginx-proxy-manager 11 | exec node --abort_on_uncaught_exception --max_old_space_size=250 index.js 12 | 13 | # vim:ft=sh:ts=4:sw=4:et:sts=4 14 | -------------------------------------------------------------------------------- /rootfs/usr/sbin/logrotate: -------------------------------------------------------------------------------- 1 | /opt/base/sbin/logrotate -------------------------------------------------------------------------------- /src/bcrypt-tool/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e # Exit immediately if a command exits with a non-zero status. 4 | set -u # Treat unset variables as an error. 5 | 6 | function log { 7 | echo ">>> $*" 8 | } 9 | 10 | BCRYPT_TOOL_VERSION="${1:-}" 11 | 12 | if [ -z "$BCRYPT_TOOL_VERSION" ]; then 13 | log "ERROR: bcrypt tool version missing." 14 | exit 1 15 | fi 16 | 17 | # 18 | # Install required packages. 19 | # 20 | 21 | apk --no-cache add \ 22 | build-base \ 23 | go \ 24 | git \ 25 | 26 | # 27 | # Compile. 28 | # 29 | 30 | log "Compiling bcrypt tool..." 31 | mkdir /tmp/go && \ 32 | env GOPATH=/tmp/go xx-go install -ldflags="-s -w" github.com/shoenig/bcrypt-tool@v$BCRYPT_TOOL_VERSION 33 | if [ ! -f /tmp/go/bin/bcrypt-tool ]; then 34 | cp -v /tmp/go/bin/*/bcrypt-tool /tmp/go/bin/bcrypt-tool 35 | fi 36 | -------------------------------------------------------------------------------- /src/nginx-proxy-manager/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e # Exit immediately if a command exits with a non-zero status. 4 | set -u # Treat unset variables as an error. 5 | 6 | # Set same default compilation flags as abuild. 7 | export CFLAGS="-Os -fomit-frame-pointer" 8 | export CXXFLAGS="$CFLAGS" 9 | export CPPFLAGS="$CFLAGS" 10 | export LDFLAGS="-Wl,--strip-all -Wl,--as-needed" 11 | 12 | export CC=xx-clang 13 | export CXX=xx-clang++ 14 | 15 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 16 | 17 | ROOTFS=/tmp/nginx-proxy-manager-install 18 | 19 | function log { 20 | echo ">>> $*" 21 | } 22 | 23 | NGINX_PROXY_MANAGER_VERSION="$1" 24 | NGINX_PROXY_MANAGER_URL="$2" 25 | 26 | if [ -z "$NGINX_PROXY_MANAGER_VERSION" ]; then 27 | log "ERROR: Nginx Proxy Manager version missing." 28 | exit 1 29 | fi 30 | 31 | if [ -z "$NGINX_PROXY_MANAGER_URL" ]; then 32 | log "ERROR: Nginx Proxy Manager URL missing." 33 | exit 1 34 | fi 35 | 36 | # 37 | # Install required packages. 38 | # 39 | 40 | apk --no-cache add \ 41 | build-base \ 42 | clang \ 43 | curl \ 44 | patch \ 45 | yarn \ 46 | git \ 47 | pythonispython3 \ 48 | npm \ 49 | bash \ 50 | 51 | xx-apk --no-cache --no-scripts add \ 52 | musl-dev \ 53 | gcc \ 54 | g++ \ 55 | 56 | # Install node-prune. 57 | log "Installing node-prune..." 58 | curl -sfL https://gobinaries.com/tj/node-prune | sh 59 | 60 | # 61 | # Download sources. 62 | # 63 | 64 | log "Downloading Nginx Proxy Manager..." 65 | mkdir /tmp/nginx-proxy-manager 66 | curl -# -L -f ${NGINX_PROXY_MANAGER_URL} | tar xz --strip 1 -C /tmp/nginx-proxy-manager 67 | 68 | # 69 | # Compile 70 | # 71 | 72 | # Set the NginxProxyManager version. 73 | sed -i "s/\"version\": \"0.0.0\",/\"version\": \"${NGINX_PROXY_MANAGER_VERSION}\",/" /tmp/nginx-proxy-manager/frontend/package.json 74 | sed -i "s/\"version\": \"0.0.0\",/\"version\": \"${NGINX_PROXY_MANAGER_VERSION}\",/" /tmp/nginx-proxy-manager/backend/package.json 75 | 76 | log "Patching Nginx Proxy Manager backend..." 77 | PATCHES=" 78 | pip-install.patch 79 | remove-certbot-dns-oci.patch 80 | " 81 | for P in $PATCHES; do 82 | echo "Applying $P..." 83 | patch -p1 -d /tmp/nginx-proxy-manager < "$SCRIPT_DIR"/"$P" 84 | done 85 | 86 | cp -r /tmp/nginx-proxy-manager /app 87 | 88 | log "Building Nginx Proxy Manager frontend..." 89 | ( 90 | cd /app/frontend 91 | yarn install --network-timeout 100000 92 | yarn build 93 | node-prune 94 | ) 95 | 96 | log "Building Nginx Proxy Manager backend..." 97 | ( 98 | # Determine the NPM architecture. 99 | # See https://nodejs.org/api/os.html#osarch 100 | case $(xx-info arch) in 101 | amd64) ARCH=x64 ;; 102 | 386) ARCH=ia32 ;; 103 | arm) ARCH=arm ;; 104 | arm64) ARCH=arm64 ;; 105 | *) echo "ERROR: Unsupported arch: $(xx-info arch)."; exit 1 ;; 106 | esac 107 | cd /app/backend 108 | # Use NPM instead of yarn because yarn doesn't seem to be able to install 109 | # for another achitecture. Note that NPM install should also use yarn.lock. 110 | npm install --legacy-peer-deps --omit=dev --omit=optional --target_platform=linux --target_arch=$ARCH 111 | node-prune 112 | rm -rf /app/backend/node_modules/sqlite3/build 113 | ) 114 | 115 | log "Installing Nginx Proxy Manager..." 116 | 117 | mkdir \ 118 | $ROOTFS \ 119 | $ROOTFS/opt \ 120 | $ROOTFS/etc \ 121 | $ROOTFS/var \ 122 | $ROOTFS/var/lib \ 123 | $ROOTFS/var/lib/nginx \ 124 | $ROOTFS/var/log \ 125 | $ROOTFS/defaults \ 126 | 127 | cp -rv /app/backend $ROOTFS/opt/nginx-proxy-manager 128 | cp -rv /app/frontend/dist $ROOTFS/opt/nginx-proxy-manager/frontend 129 | cp -rv /app/global $ROOTFS/opt/nginx-proxy-manager/global 130 | 131 | mkdir $ROOTFS/opt/nginx-proxy-manager/bin 132 | cp -rv /tmp/nginx-proxy-manager/docker/rootfs/etc/nginx $ROOTFS/etc/ 133 | cp -rv /tmp/nginx-proxy-manager/docker/rootfs/var/www $ROOTFS/var/ 134 | cp -rv /tmp/nginx-proxy-manager/docker/rootfs/etc/letsencrypt.ini $ROOTFS/etc/ 135 | cp -rv /tmp/nginx-proxy-manager/docker/rootfs/etc/logrotate.d $ROOTFS/etc/ 136 | 137 | # Remove the nginx development config. 138 | rm $ROOTFS/etc/nginx/conf.d/dev.conf 139 | 140 | # Change the management interface port to the unprivileged port 8181. 141 | sed -i 's|81 default|8181 default|' $ROOTFS/etc/nginx/conf.d/production.conf 142 | 143 | # Change the management interface root. 144 | sed -i 's|/app/frontend;|/opt/nginx-proxy-manager/frontend;|' $ROOTFS/etc/nginx/conf.d/production.conf 145 | 146 | # Change the HTTP port 80 to the unprivileged port 8080. 147 | sed -i 's|80;|8080;|' $ROOTFS/etc/nginx/conf.d/default.conf 148 | sed -i 's|"80";|"8080";|' $ROOTFS/etc/nginx/conf.d/default.conf 149 | sed -i 's|listen 80;|listen 8080;|' $ROOTFS/opt/nginx-proxy-manager/templates/letsencrypt-request.conf 150 | sed -i 's|:80;|:8080;|' $ROOTFS/opt/nginx-proxy-manager/templates/letsencrypt-request.conf 151 | sed -i 's|listen 80;|listen 8080;|' $ROOTFS/opt/nginx-proxy-manager/templates/_listen.conf 152 | sed -i 's|:80;|:8080;|' $ROOTFS/opt/nginx-proxy-manager/templates/_listen.conf 153 | sed -i 's|80 default;|8080 default;|' $ROOTFS/opt/nginx-proxy-manager/templates/default.conf 154 | 155 | # Change the HTTPs port 443 to the unprivileged port 4443. 156 | sed -i 's|443 |4443 |' $ROOTFS/etc/nginx/conf.d/default.conf 157 | sed -i 's|"443";|"4443";|' $ROOTFS/etc/nginx/conf.d/default.conf 158 | sed -i 's|listen 443 |listen 4443 |' $ROOTFS/opt/nginx-proxy-manager/templates/_listen.conf 159 | sed -i 's|:443 |:4443 |' $ROOTFS/opt/nginx-proxy-manager/templates/_listen.conf 160 | sed -i 's|:443;|:4443;|' $ROOTFS/opt/nginx-proxy-manager/templates/_listen.conf 161 | 162 | # Fix nginx test command line. 163 | sed -i 's|-g "error_log off;"||' $ROOTFS/opt/nginx-proxy-manager/internal/nginx.js 164 | 165 | # Remove the `user` directive, since we want nginx to run as non-root. 166 | sed -i 's|user npm;|#user npm;|' $ROOTFS/etc/nginx/nginx.conf 167 | 168 | # Change client_body_temp_path. 169 | sed -i 's|/tmp/nginx/body|/var/tmp/nginx/body|' $ROOTFS/etc/nginx/nginx.conf 170 | 171 | # Fix the logrotate config. 172 | sed -i 's|npm npm|app app|' $ROOTFS/etc/logrotate.d/nginx-proxy-manager 173 | sed -i 's|/run/nginx.pid|/run/nginx/nginx.pid|' $ROOTFS/etc/logrotate.d/nginx-proxy-manager 174 | sed -i 's|logrotate /etc/logrotate.d/nginx-proxy-manager|logrotate -s /config/logrotate.status /etc/logrotate.d/nginx-proxy-manager|' $ROOTFS/opt/nginx-proxy-manager/setup.js 175 | sed -i 's|/data/logs/\*/access.log|/data/logs/access.log|' $ROOTFS/etc/logrotate.d/nginx-proxy-manager 176 | sed -i 's|/data/logs/\*/error.log|/data/logs/error.log|' $ROOTFS/etc/logrotate.d/nginx-proxy-manager 177 | 178 | # Redirect `/data' to '/config'. 179 | ln -s /config $ROOTFS/data 180 | 181 | # Make sure the config file for IP ranges is stored in persistent volume. 182 | mv $ROOTFS/etc/nginx/conf.d/include/ip_ranges.conf $ROOTFS/defaults/ 183 | ln -sf /config/nginx/ip_ranges.conf $ROOTFS/etc/nginx/conf.d/include/ip_ranges.conf 184 | 185 | # Make sure the config file for resolvers is stored in persistent volume. 186 | ln -sf /config/nginx/resolvers.conf $ROOTFS/etc/nginx/conf.d/include/resolvers.conf 187 | 188 | # Make sure nginx cache is stored on the persistent volume. 189 | ln -s /config/nginx/cache $ROOTFS/var/lib/nginx/cache 190 | 191 | # Make sure the manager config file is stored in persistent volume. 192 | rm -r $ROOTFS/opt/nginx-proxy-manager/config 193 | mkdir $ROOTFS/opt/nginx-proxy-manager/config 194 | ln -s /config/production.json $ROOTFS/opt/nginx-proxy-manager/config/production.json 195 | 196 | # Make sure letsencrypt certificates are stored in persistent volume. 197 | ln -s /config/letsencrypt $ROOTFS/etc/letsencrypt 198 | 199 | # Cleanup. 200 | find $ROOTFS/opt/nginx-proxy-manager -name "*.h" -delete 201 | find $ROOTFS/opt/nginx-proxy-manager -name "*.cc" -delete 202 | find $ROOTFS/opt/nginx-proxy-manager -name "*.c" -delete 203 | find $ROOTFS/opt/nginx-proxy-manager -name "*.gyp" -delete 204 | -------------------------------------------------------------------------------- /src/nginx-proxy-manager/pip-install.patch: -------------------------------------------------------------------------------- 1 | --- a/backend/lib/certbot.js 2 | +++ b/backend/lib/certbot.js 3 | @@ -63,7 +63,7 @@ 4 | plugin.version = plugin.version.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT); 5 | plugin.dependencies = plugin.dependencies.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT); 6 | 7 | - const cmd = '. /opt/certbot/bin/activate && pip install --no-cache-dir ' + plugin.dependencies + ' ' + plugin.package_name + plugin.version + ' ' + ' && deactivate'; 8 | + const cmd = 'pip install --no-cache-dir ' + plugin.dependencies + ' ' + plugin.package_name + plugin.version; 9 | return utils.exec(cmd) 10 | .then((result) => { 11 | logger.complete(`Installed ${pluginKey}`); 12 | -------------------------------------------------------------------------------- /src/nginx-proxy-manager/remove-certbot-dns-oci.patch: -------------------------------------------------------------------------------- 1 | Because of the Oracle Cloud Infrastructure DNS plugin dependencies, installing 2 | it causes certbot to be downgraded, which then break any execution of certbot. 3 | --- a/global/certbot-dns-plugins.json 4 | +++ b/global/certbot-dns-plugins.json 5 | @@ -437,14 +437,6 @@ 6 | "credentials": "dns_nsone_api_key = MDAwMDAwMDAwMDAwMDAw", 7 | "full_plugin_name": "dns-nsone" 8 | }, 9 | - "oci": { 10 | - "name": "Oracle Cloud Infrastructure DNS", 11 | - "package_name": "certbot-dns-oci", 12 | - "version": "~=0.3.6", 13 | - "dependencies": "oci", 14 | - "credentials": "[DEFAULT]\nuser = ocid1.user.oc1...\nfingerprint = xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx\ntenancy = ocid1.tenancy.oc1...\nregion = us-ashburn-1\nkey_file = ~/.oci/oci_api_key.pem", 15 | - "full_plugin_name": "dns-oci" 16 | - }, 17 | "ovh": { 18 | "name": "OVH", 19 | "package_name": "certbot-dns-ovh", 20 | -------------------------------------------------------------------------------- /src/openresty/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e # Exit immediately if a command exits with a non-zero status. 4 | set -u # Treat unset variables as an error. 5 | 6 | # Set same default compilation flags as abuild. 7 | export CFLAGS="-Os -fomit-frame-pointer" 8 | export CXXFLAGS="$CFLAGS" 9 | export CPPFLAGS="$CFLAGS" 10 | export LDFLAGS="-Wl,--strip-all -Wl,--as-needed" 11 | 12 | export CC=xx-clang 13 | export CXX=xx-clang++ 14 | 15 | function log { 16 | echo ">>> $*" 17 | } 18 | 19 | OPENRESTY_URL="${1:-}" 20 | NGINX_HTTP_GEOIP2_MODULE_URL="${2:-}" 21 | LIBMAXMINDDB_URL="${3:-}" 22 | 23 | if [ -z "$OPENRESTY_URL" ]; then 24 | log "ERROR: OpenResty URL missing." 25 | exit 1 26 | fi 27 | 28 | if [ -z "$NGINX_HTTP_GEOIP2_MODULE_URL" ]; then 29 | log "ERROR: Nginx HTTP GeoIP2 module URL missing." 30 | exit 1 31 | fi 32 | 33 | if [ -z "$LIBMAXMINDDB_URL" ]; then 34 | log "ERROR: libmaxminddb URL missing." 35 | exit 1 36 | fi 37 | 38 | # 39 | # Install required packages. 40 | # 41 | 42 | apk --no-cache add \ 43 | curl \ 44 | build-base \ 45 | clang \ 46 | perl \ 47 | file \ 48 | 49 | xx-apk --no-cache --no-scripts add \ 50 | musl-dev \ 51 | gcc \ 52 | g++ \ 53 | linux-headers \ 54 | pcre-dev \ 55 | openssl-dev \ 56 | zlib-dev \ 57 | luajit-dev \ 58 | 59 | # 60 | # Download sources. 61 | # 62 | 63 | log "Downloading OpenResty..." 64 | mkdir /tmp/openresty 65 | curl -# -L -f ${OPENRESTY_URL} | tar xz --strip 1 -C /tmp/openresty 66 | 67 | log "Downloading GeoIP2 module..." 68 | mkdir /tmp/ngx_http_geoip2_module 69 | curl -# -L -f ${NGINX_HTTP_GEOIP2_MODULE_URL} | tar xz --strip 1 -C /tmp/ngx_http_geoip2_module 70 | 71 | log "Downloading libmaxminddb..." 72 | mkdir /tmp/libmaxminddb 73 | curl -# -L -f ${LIBMAXMINDDB_URL} | tar xz --strip 1 -C /tmp/libmaxminddb 74 | 75 | # 76 | # Compile. 77 | # 78 | 79 | log "Configuring libmaxminddb..." 80 | ( 81 | cd /tmp/libmaxminddb && ./configure \ 82 | --build=$(TARGETPLATFORM= xx-clang --print-target-triple) \ 83 | --host=$(xx-clang --print-target-triple) \ 84 | --prefix=/usr \ 85 | --with-pic \ 86 | --enable-shared=no \ 87 | --enable-static=yes \ 88 | ) 89 | 90 | log "Compiling libmaxminddb..." 91 | make -C /tmp/libmaxminddb -j$(nproc) 92 | 93 | log "Installing libmaxminddb..." 94 | make DESTDIR=$(xx-info sysroot) -C /tmp/libmaxminddb install 95 | 96 | log "Patching OpenResty..." 97 | # Patch Nginx for cross-compile support. See the Yocto Nginx recipe: https://github.com/openembedded/meta-openembedded/tree/master/meta-webserver/recipes-httpd/nginx 98 | NGINX_SRC_DIR="$(find /tmp/openresty/bundle -mindepth 1 -maxdepth 1 -type d -name 'nginx-*')" 99 | curl -# -L -f https://github.com/openembedded/meta-openembedded/raw/master/meta-webserver/recipes-httpd/nginx/files/nginx-cross.patch | patch -p1 -d "$NGINX_SRC_DIR" 100 | curl -# -L -f https://github.com/openembedded/meta-openembedded/raw/master/meta-webserver/recipes-httpd/nginx/files/0001-Allow-the-overriding-of-the-endianness-via-the-confi.patch | patch -p1 -d "$NGINX_SRC_DIR" 101 | 102 | case "$(xx-info arch)" in 103 | amd64) PTRSIZE=8; ENDIANNESS=little ;; 104 | arm64) PTRSIZE=8; ENDIANNESS=little ;; 105 | 386) PTRSIZE=4; ENDIANNESS=little ;; 106 | arm) PTRSIZE=4; ENDIANNESS=little ;; 107 | *) echo "Unknown ARCH: $(xx-info arch)" ; exit 1 ;; 108 | esac 109 | 110 | log "Configuring OpenResty..." 111 | ( 112 | cd /tmp/openresty && ./configure -j$(nproc) \ 113 | --crossbuild=Linux:$(xx-info arch) \ 114 | --with-cc="xx-clang" \ 115 | --with-cc-opt="-Os -fomit-frame-pointer -Wno-sign-compare" \ 116 | --with-ld-opt="-Wl,--strip-all -Wl,--as-needed" \ 117 | --with-luajit=$(xx-info sysroot)usr \ 118 | --prefix=/var/lib/nginx \ 119 | --sbin-path=/usr/sbin/nginx \ 120 | --modules-path=/usr/lib/nginx/modules \ 121 | --conf-path=/etc/nginx/nginx.conf \ 122 | --pid-path=/var/run/nginx/nginx.pid \ 123 | --lock-path=/var/run/nginx/nginx.lock \ 124 | --error-log-path=/config/log/error.log \ 125 | --http-log-path=/config/log/access.log \ 126 | \ 127 | --http-client-body-temp-path=/var/tmp/nginx/client_body \ 128 | --http-proxy-temp-path=/var/tmp/nginx/proxy \ 129 | --http-fastcgi-temp-path=/var/tmp/nginx/fastcgi \ 130 | --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi \ 131 | --http-scgi-temp-path=/var/tmp/nginx/scgi \ 132 | --with-perl_modules_path=/usr/lib/perl5/vendor_perl \ 133 | \ 134 | --user=app \ 135 | --group=app \ 136 | --with-threads \ 137 | --with-file-aio \ 138 | \ 139 | --with-endian=$ENDIANNESS \ 140 | --with-int=4 \ 141 | --with-long=${PTRSIZE} \ 142 | --with-long-long=8 \ 143 | --with-ptr-size=${PTRSIZE} \ 144 | --with-sig-atomic-t=4 \ 145 | --with-size-t=${PTRSIZE} \ 146 | --with-off-t=8 \ 147 | --with-time-t=${PTRSIZE} \ 148 | --with-sys-nerr=132 \ 149 | \ 150 | --with-http_ssl_module \ 151 | --with-http_v2_module \ 152 | --with-http_realip_module \ 153 | --with-http_addition_module \ 154 | --with-http_sub_module \ 155 | --with-http_dav_module \ 156 | --with-http_flv_module \ 157 | --with-http_mp4_module \ 158 | --with-http_gunzip_module \ 159 | --with-http_gzip_static_module \ 160 | --with-http_auth_request_module \ 161 | --with-http_random_index_module \ 162 | --with-http_secure_link_module \ 163 | --with-http_degradation_module \ 164 | --with-http_slice_module \ 165 | --with-http_stub_status_module \ 166 | --with-mail \ 167 | --with-mail_ssl_module \ 168 | --with-stream \ 169 | --with-stream_ssl_module \ 170 | --with-stream_realip_module \ 171 | --with-stream_ssl_preread_module \ 172 | --with-pcre-jit \ 173 | \ 174 | --add-module=/tmp/ngx_http_geoip2_module \ 175 | ) 176 | 177 | log "Compiling OpenResty..." 178 | make -C /tmp/openresty -j$(nproc) 179 | 180 | log "Installing OpenResty..." 181 | make DESTDIR=/tmp/openresty-install -C /tmp/openresty install 182 | 183 | rm -r \ 184 | /tmp/openresty-install/etc/nginx/*.default \ 185 | /tmp/openresty-install/var/lib/nginx/bin/opm \ 186 | /tmp/openresty-install/var/lib/nginx/bin/nginx-xml2pod \ 187 | /tmp/openresty-install/var/lib/nginx/bin/restydoc-index \ 188 | /tmp/openresty-install/var/lib/nginx/bin/restydoc \ 189 | /tmp/openresty-install/var/lib/nginx/bin/md2pod.pl \ 190 | /tmp/openresty-install/var/lib/nginx/pod \ 191 | /tmp/openresty-install/var/lib/nginx/resty.index \ 192 | /tmp/openresty-install/var/lib/nginx/site \ 193 | /tmp/openresty-install/var/run \ 194 | --------------------------------------------------------------------------------