├── .gitattributes ├── scripts ├── docker-login.sh ├── docker-push.sh └── docker-build.sh ├── test ├── test.dockerfile ├── app │ └── main.py └── hypercorn_conf.py ├── images ├── app │ ├── prestart.sh │ └── main.py ├── python3.9.dockerfile ├── python3.9-slim.dockerfile ├── python3.7.dockerfile ├── python3.8.dockerfile ├── python3.7-slim.dockerfile ├── python3.8-slim.dockerfile ├── start-reload.sh ├── python3.9-alpine.dockerfile ├── python3.7-alpine.dockerfile ├── python3.8-alpine.dockerfile ├── start.sh └── hypercorn_conf.py ├── LICENSE ├── .github └── workflows │ └── deploy.yml ├── .gitignore └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | test/* -linguist-detectable 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /scripts/docker-login.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | set -e 4 | 5 | echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin 6 | -------------------------------------------------------------------------------- /test/test.dockerfile: -------------------------------------------------------------------------------- 1 | FROM bynect/hypercorn-fastapi:python3.8-slim 2 | 3 | ENV TCP_PORT=8000 WORKER_CLASS=trio USE_TCP=true 4 | 5 | COPY ./app /app 6 | -------------------------------------------------------------------------------- /test/app/main.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | 3 | app = FastAPI() 4 | 5 | 6 | @app.get("/") 7 | async def root(): 8 | return {"message": "Test"} 9 | -------------------------------------------------------------------------------- /images/app/prestart.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env sh 2 | 3 | echo "Inside the prestart script" 4 | #put here a script that needs to be executed before hypercorn starts 5 | -------------------------------------------------------------------------------- /scripts/docker-push.sh: -------------------------------------------------------------------------------- 1 | #! usr/bin/env bash 2 | 3 | set -e 4 | 5 | tag="bynect/hypercorn-fastapi:$NAME" 6 | 7 | bash scripts/docker-build.sh 8 | bash scripts/docker-login.sh 9 | 10 | docker push "$tag" 11 | -------------------------------------------------------------------------------- /scripts/docker-build.sh: -------------------------------------------------------------------------------- 1 | #! usr/bin/env bash 2 | 3 | set -e 4 | 5 | tag="bynect/hypercorn-fastapi:$NAME" 6 | DOCKERFILE="$NAME" 7 | 8 | if [ "$NAME" == "latest" ] 9 | then 10 | DOCKERFILE="python3.8" 11 | fi 12 | 13 | docker build -t "$tag" --file "./images/${DOCKERFILE}.dockerfile" "./images/" 14 | -------------------------------------------------------------------------------- /images/app/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from fastapi import FastAPI 3 | 4 | app = FastAPI() 5 | version = "{0}.{1}".format(sys.version_info.major, sys.version_info.minor) 6 | 7 | @app.get("/") 8 | async def index(): 9 | return "Hello, World. From a FastAPI app running on Hypercorn and Python {}.".format(version) 10 | -------------------------------------------------------------------------------- /images/python3.9.dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9 2 | 3 | LABEL author="bynect " 4 | 5 | RUN python3 -m pip install hypercorn hypercorn[uvloop] aioquic hypercorn[h3] fastapi \ 6 | --no-cache-dir --no-color --no-python-version-warning --disable-pip-version-check 7 | 8 | COPY ./start.sh /start.sh 9 | RUN chmod +x /start.sh 10 | 11 | COPY ./start-reload.sh /start-reload.sh 12 | RUN chmod +x /start-reload.sh 13 | 14 | COPY ./hypercorn_conf.py /hypercorn_conf.py 15 | 16 | COPY ./app /app 17 | 18 | WORKDIR /app 19 | ENV PYTHONPATH=/app 20 | 21 | EXPOSE 80 22 | EXPOSE 443 23 | 24 | CMD ["/start.sh"] 25 | -------------------------------------------------------------------------------- /images/python3.9-slim.dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-slim 2 | 3 | LABEL author="bynect " 4 | 5 | RUN python3 -m pip install hypercorn hypercorn[uvloop] aioquic hypercorn[h3] fastapi \ 6 | --no-cache-dir --no-color --no-python-version-warning --disable-pip-version-check 7 | 8 | COPY ./start.sh /start.sh 9 | RUN chmod +x /start.sh 10 | 11 | COPY ./start-reload.sh /start-reload.sh 12 | RUN chmod +x /start-reload.sh 13 | 14 | COPY ./hypercorn_conf.py /hypercorn_conf.py 15 | 16 | COPY ./app /app 17 | 18 | WORKDIR /app 19 | ENV PYTHONPATH=/app 20 | 21 | EXPOSE 80 22 | EXPOSE 443 23 | 24 | CMD ["/start.sh"] 25 | -------------------------------------------------------------------------------- /images/python3.7.dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7 2 | 3 | LABEL author="bynect " 4 | 5 | RUN python3 -m pip install hypercorn hypercorn[uvloop] hypercorn[trio] trio aioquic hypercorn[h3] fastapi \ 6 | --no-cache-dir --no-color --no-python-version-warning --disable-pip-version-check 7 | 8 | COPY ./start.sh /start.sh 9 | RUN chmod +x /start.sh 10 | 11 | COPY ./start-reload.sh /start-reload.sh 12 | RUN chmod +x /start-reload.sh 13 | 14 | COPY ./hypercorn_conf.py /hypercorn_conf.py 15 | 16 | COPY ./app /app 17 | 18 | WORKDIR /app 19 | ENV PYTHONPATH=/app 20 | 21 | EXPOSE 80 22 | EXPOSE 443 23 | 24 | CMD ["/start.sh"] 25 | -------------------------------------------------------------------------------- /images/python3.8.dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | LABEL author="bynect " 4 | 5 | RUN python3 -m pip install hypercorn hypercorn[uvloop] hypercorn[trio] trio aioquic hypercorn[h3] fastapi \ 6 | --no-cache-dir --no-color --no-python-version-warning --disable-pip-version-check 7 | 8 | COPY ./start.sh /start.sh 9 | RUN chmod +x /start.sh 10 | 11 | COPY ./start-reload.sh /start-reload.sh 12 | RUN chmod +x /start-reload.sh 13 | 14 | COPY ./hypercorn_conf.py /hypercorn_conf.py 15 | 16 | COPY ./app /app 17 | 18 | WORKDIR /app 19 | ENV PYTHONPATH=/app 20 | 21 | EXPOSE 80 22 | EXPOSE 443 23 | 24 | CMD ["/start.sh"] 25 | -------------------------------------------------------------------------------- /images/python3.7-slim.dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-slim 2 | 3 | LABEL author="bynect " 4 | 5 | RUN python3 -m pip install hypercorn hypercorn[uvloop] hypercorn[trio] trio aioquic hypercorn[h3] fastapi \ 6 | --no-cache-dir --no-color --no-python-version-warning --disable-pip-version-check 7 | 8 | COPY ./start.sh /start.sh 9 | RUN chmod +x /start.sh 10 | 11 | COPY ./start-reload.sh /start-reload.sh 12 | RUN chmod +x /start-reload.sh 13 | 14 | COPY ./hypercorn_conf.py /hypercorn_conf.py 15 | 16 | COPY ./app /app 17 | 18 | WORKDIR /app 19 | ENV PYTHONPATH=/app 20 | 21 | EXPOSE 80 22 | EXPOSE 443 23 | 24 | CMD ["/start.sh"] 25 | -------------------------------------------------------------------------------- /images/python3.8-slim.dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-slim 2 | 3 | LABEL author="bynect " 4 | 5 | RUN python3 -m pip install hypercorn hypercorn[uvloop] hypercorn[trio] trio aioquic hypercorn[h3] fastapi \ 6 | --no-cache-dir --no-color --no-python-version-warning --disable-pip-version-check 7 | 8 | COPY ./start.sh /start.sh 9 | RUN chmod +x /start.sh 10 | 11 | COPY ./start-reload.sh /start-reload.sh 12 | RUN chmod +x /start-reload.sh 13 | 14 | COPY ./hypercorn_conf.py /hypercorn_conf.py 15 | 16 | COPY ./app /app 17 | 18 | WORKDIR /app 19 | ENV PYTHONPATH=/app 20 | 21 | EXPOSE 80 22 | EXPOSE 443 23 | 24 | CMD ["/start.sh"] 25 | -------------------------------------------------------------------------------- /images/start-reload.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env sh 2 | 3 | set -e 4 | 5 | if [ -f /app/app/main.py ] 6 | then 7 | DEFAULT_MODULE_NAME=app.main 8 | elif [ -f /app/main.py ] 9 | then 10 | DEFAULT_MODULE_NAME=main 11 | fi 12 | 13 | MODULE_NAME=${MODULE_NAME:-$DEFAULT_MODULE_NAME} 14 | VARIABLE_NAME=${VARIABLE_NAME:-app} 15 | export APP_MODULE=${APP_MODULE:-"$MODULE_NAME:$VARIABLE_NAME"} 16 | 17 | HOST=${HOST:-0.0.0.0} 18 | TCP_PORT=${TCP_PORT:-80} 19 | BIND=${BIND:-"$HOST:$TCP_PORT"} 20 | 21 | LOG_LEVEL=${LOG_LEVEL:-info} 22 | 23 | PRE_START_PATH=${PRE_START_PATH:-/app/prestart.sh} 24 | if [ -f $PRE_START_PATH ] 25 | then 26 | echo "Running script $PRE_START_PATH" 27 | . "$PRE_START_PATH" 28 | else 29 | echo "There is no script $PRE_START_PATH" 30 | fi 31 | 32 | exec hypercorn --debug --reload --bind $BIND --log-level $LOG_LEVEL "$APP_MODULE" 33 | -------------------------------------------------------------------------------- /images/python3.9-alpine.dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-alpine 2 | 3 | LABEL author="bynect " 4 | 5 | RUN apk add --no-cache --virtual .build-deps gcc libc-dev libffi-dev openssl-dev \ 6 | build-base bsd-compat-headers make musl-dev python3-dev cargo \ 7 | && python3 -m pip install --upgrade pip \ 8 | && python3 -m pip install hypercorn hypercorn[uvloop] aioquic hypercorn[h3] fastapi \ 9 | --no-cache-dir --no-color --no-python-version-warning --disable-pip-version-check \ 10 | && apk del .build-deps gcc libc-dev make 11 | 12 | COPY ./start.sh /start.sh 13 | RUN chmod +x /start.sh 14 | 15 | COPY ./start-reload.sh /start-reload.sh 16 | RUN chmod +x /start-reload.sh 17 | 18 | COPY ./hypercorn_conf.py /hypercorn_conf.py 19 | 20 | COPY ./app /app 21 | 22 | WORKDIR /app 23 | ENV PYTHONPATH=/app 24 | 25 | EXPOSE 80 26 | EXPOSE 443 27 | 28 | CMD ["/start.sh"] 29 | -------------------------------------------------------------------------------- /images/python3.7-alpine.dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | 3 | LABEL author="bynect " 4 | 5 | RUN apk add --no-cache --virtual .build-deps gcc libc-dev libffi-dev openssl-dev \ 6 | build-base bsd-compat-headers make musl-dev python3-dev cargo \ 7 | && python3 -m pip install --upgrade pip \ 8 | && python3 -m pip install hypercorn hypercorn[uvloop] hypercorn[trio] trio aioquic hypercorn[h3] fastapi \ 9 | --no-cache-dir --no-color --no-python-version-warning --disable-pip-version-check \ 10 | && apk del .build-deps gcc libc-dev make 11 | 12 | COPY ./start.sh /start.sh 13 | RUN chmod +x /start.sh 14 | 15 | COPY ./start-reload.sh /start-reload.sh 16 | RUN chmod +x /start-reload.sh 17 | 18 | COPY ./hypercorn_conf.py /hypercorn_conf.py 19 | 20 | COPY ./app /app 21 | 22 | WORKDIR /app 23 | ENV PYTHONPATH=/app 24 | 25 | EXPOSE 80 26 | EXPOSE 443 27 | 28 | CMD ["/start.sh"] 29 | -------------------------------------------------------------------------------- /images/python3.8-alpine.dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-alpine 2 | 3 | LABEL author="bynect " 4 | 5 | RUN apk add --no-cache --virtual .build-deps gcc libc-dev libffi-dev openssl-dev \ 6 | build-base bsd-compat-headers make musl-dev python3-dev cargo \ 7 | && python3 -m pip install --upgrade pip \ 8 | && python3 -m pip install hypercorn hypercorn[uvloop] hypercorn[trio] trio aioquic hypercorn[h3] fastapi \ 9 | --no-cache-dir --no-color --no-python-version-warning --disable-pip-version-check \ 10 | && apk del .build-deps gcc libc-dev make 11 | 12 | COPY ./start.sh /start.sh 13 | RUN chmod +x /start.sh 14 | 15 | COPY ./start-reload.sh /start-reload.sh 16 | RUN chmod +x /start-reload.sh 17 | 18 | COPY ./hypercorn_conf.py /hypercorn_conf.py 19 | 20 | COPY ./app /app 21 | 22 | WORKDIR /app 23 | ENV PYTHONPATH=/app 24 | 25 | EXPOSE 80 26 | EXPOSE 443 27 | 28 | CMD ["/start.sh"] 29 | -------------------------------------------------------------------------------- /images/start.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env sh 2 | 3 | set -e 4 | 5 | if [ -f /app/app/main.py ] 6 | then 7 | DEFAULT_MODULE_NAME=app.main 8 | elif [ -f /app/main.py ] 9 | then 10 | DEFAULT_MODULE_NAME=main 11 | fi 12 | 13 | MODULE_NAME=${MODULE_NAME:-$DEFAULT_MODULE_NAME} 14 | VARIABLE_NAME=${VARIABLE_NAME:-app} 15 | export APP_MODULE=${APP_MODULE:-"$MODULE_NAME:$VARIABLE_NAME"} 16 | 17 | if [ -f /app/app/hypercorn_conf.py ] 18 | then 19 | DEFAULT_CONF=file:/app/app/hypercorn_conf.py 20 | elif [ -f /app/hypercorn_conf.py ] 21 | then 22 | DEFAULT_CONF=file:/app/hypercorn_conf.py 23 | else 24 | DEFAULT_CONF=file:/hypercorn_conf.py 25 | fi 26 | 27 | export HYPERCORN_CONF=${HYPERCORN_CONF:-$DEFAULT_CONF} 28 | 29 | PRE_START_PATH=${PRE_START_PATH:-/app/prestart.sh} 30 | if [ -f $PRE_START_PATH ] 31 | then 32 | echo "Running script $PRE_START_PATH" 33 | . "$PRE_START_PATH" 34 | else 35 | echo "There is no script $PRE_START_PATH" 36 | fi 37 | 38 | exec hypercorn -c "$HYPERCORN_CONF" "$APP_MODULE" 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020-2021 @bynect 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'images/*' 9 | workflow_dispatch: 10 | 11 | jobs: 12 | deploy: 13 | strategy: 14 | matrix: 15 | image: 16 | - name: latest 17 | python_version: "3.9" 18 | - name: python3.9 19 | python_version: "3.9" 20 | - name: python3.9-slim 21 | python_version: "3.9" 22 | - name: python3.9-alpine 23 | python_version: "3.9" 24 | 25 | - name: python3.8 26 | python_version: "3.8" 27 | - name: python3.8-slim 28 | python_version: "3.8" 29 | - name: python3.8-alpine 30 | python_version: "3.8" 31 | 32 | - name: python3.7 33 | python_version: "3.7" 34 | - name: python3.7-slim 35 | python_version: "3.7" 36 | - name: python3.7-alpine 37 | python_version: "3.7" 38 | fail-fast: true 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v2 42 | - name: Set up Python 43 | uses: actions/setup-python@v1 44 | with: 45 | python-version: "3.7" 46 | - name: Install Dependencies 47 | run: python3.7 -m pip install docker 48 | - name: Deploy Image 49 | run: bash scripts/docker-push.sh 50 | env: 51 | NAME: ${{ matrix.image.name }} 52 | DOCKERFILE: ${{ matrix.image.dockerfile }} 53 | PYTHON_VERSION: ${{ matrix.image.python_version }} 54 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} 55 | DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} 56 | -------------------------------------------------------------------------------- /test/hypercorn_conf.py: -------------------------------------------------------------------------------- 1 | import os 2 | import multiprocessing 3 | import json 4 | 5 | workers_per_core = os.getenv("WORKERS_PER_CORE", "1") 6 | use_workers_per_core = float(workers_per_core) 7 | 8 | web_concurrency = os.getenv("WEB_CONCURRENCY", None) 9 | 10 | max_workers = os.getenv("MAX_WORKERS", None) 11 | if max_workers: 12 | use_max_workers = int(max_workers) 13 | else: 14 | use_max_workers = None 15 | 16 | host = os.getenv("HOST", "0.0.0.0") 17 | ssl_port = os.getenv("SSL_PORT", "443") 18 | tcp_port = os.getenv("TCP_PORT", "80") 19 | 20 | bind_env = os.getenv("BIND", None) 21 | if bind_env: 22 | use_bind = bind_env 23 | else: 24 | use_bind = "{0}:{1}".format(host, ssl_port) 25 | 26 | insecure_bind_env = os.getenv("INSECURE_BIND", None) 27 | if insecure_bind_env: 28 | use_insecure_bind = insecure_bind_env 29 | else: 30 | use_insecure_bind = "{0}:{1}".format(host, tcp_port) 31 | 32 | use_log_level = os.getenv("LOG_LEVEL", "info") 33 | 34 | cores = multiprocessing.cpu_count() 35 | default_web_concurrency = use_workers_per_core * cores 36 | if web_concurrency: 37 | use_web_concurrency = int(web_concurrency) 38 | assert web_concurrency > 0 39 | else: 40 | use_web_concurrency = max(int(default_web_concurrency), 2) 41 | if use_max_workers: 42 | use_web_concurrency = min(use_web_concurrency, use_max_workers) 43 | 44 | accesslog_var = os.getenv("ACCESS_LOG", "-") 45 | use_accesslog = accesslog_var or None 46 | 47 | errorlog_var = os.getenv("ERROR_LOG", "-") 48 | use_errorlog = errorlog_var or None 49 | 50 | use_graceful_timeout = os.getenv("GRACEFUL_TIMEOUT", "120") 51 | 52 | use_timeout = os.getenv("TIMEOUT", "120") 53 | 54 | use_keepalive = os.getenv("KEEP_ALIVE", "5") 55 | 56 | #env 57 | loglevel = use_log_level 58 | workers = use_web_concurrency 59 | bind = use_insecure_bind 60 | errorlog = use_errorlog 61 | accesslog = use_accesslog 62 | graceful_timeout = int(use_graceful_timeout) 63 | ssl_handshake_timeout = int(use_timeout) 64 | keepalive = int(use_keepalive) 65 | 66 | log_data = { 67 | "loglevel": loglevel, 68 | "workers": workers, 69 | "bind": bind, 70 | "graceful_timeout": graceful_timeout, 71 | "timeout": ssl_handshake_timeout, 72 | "keepalive": keepalive, 73 | "errorlog": errorlog, 74 | "accesslog": accesslog, 75 | 76 | "workers_per_core": workers_per_core, 77 | "use_max_workers": use_max_workers, 78 | "host": host, 79 | "port": tcp_port 80 | } 81 | 82 | print(json.dumps(log_data, indent = 4), flush=True) 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ 139 | 140 | 141 | # Others 142 | .vscode/ 143 | -------------------------------------------------------------------------------- /images/hypercorn_conf.py: -------------------------------------------------------------------------------- 1 | import os 2 | import multiprocessing 3 | import json 4 | 5 | #utils 6 | def booleanize(value) -> bool: 7 | if value is None: 8 | return False 9 | 10 | falsy = ["no", "n", "0", "false"] 11 | truly = ["yes", "y", "1", "true"] 12 | 13 | if value.lower() in falsy: 14 | return False 15 | elif value.lower() in truly: 16 | return True 17 | else: 18 | raise TypeError("Non boolean-like value {}".format(value)) 19 | 20 | 21 | #ssl opts 22 | use_ssl = booleanize(os.getenv("USE_SSL", "False")) 23 | use_tcp = booleanize(os.getenv("USE_TCP", "True")) 24 | #assert any([use_ssl, use_tcp]), "At least one of USE_SSL and USE_TCP must be set" 25 | 26 | use_certfile = os.getenv("CERTFILE", None) 27 | use_ca_certs = os.getenv("CA_CERTS", None) 28 | use_ciphers = os.getenv("CIPHERS", "ECDHE+AESGCM") 29 | use_keyfile = os.getenv("KEYFILE", None) 30 | #assert not(use_ssl and any([use_certfile, use_keyfile, use_ca_certs])), "USE_SSL Requires CERTFILE/KEYFILE/CA_CERTS" 31 | 32 | 33 | #binding 34 | host = os.getenv("HOST", "0.0.0.0") 35 | ssl_port = os.getenv("SSL_PORT", "443") 36 | tcp_port = os.getenv("TCP_PORT", "80") 37 | if use_ssl: 38 | assert ssl_port != tcp_port, "SSL_PORT Must be different than TCP_PORT" 39 | 40 | use_quic_bind = os.getenv("QUIC_BIND", None) 41 | 42 | use_insecure_bind = os.getenv("INSECURE_BIND", None) 43 | 44 | #assert not(bool(use_insecure_bind) != all([use_ssl, use_tcp])), "INSECURE_BIND Must be used only when USE_SSL and USE_TCP are both set" 45 | if use_ssl and use_tcp: 46 | if not use_insecure_bind: 47 | use_insecure_bind = "{}:{}".format(host, tcp_port) 48 | 49 | use_bind = os.getenv("BIND", None) 50 | if not use_bind: 51 | use_bind = "{}:{}".format(host, ssl_port if use_ssl else tcp_port) 52 | 53 | 54 | #workers 55 | cores = multiprocessing.cpu_count() 56 | workers_multiplier = float(os.getenv("WORKERS_PER_CORE", "1")) 57 | workers_max = os.getenv("MAX_WORKERS", None) 58 | if workers_max: 59 | workers_max = int(workers_max) 60 | 61 | default_web_concurrency = cores * workers_multiplier 62 | web_concurrency = web_concurrency = os.getenv("WEB_CONCURRENCY", None) 63 | 64 | if web_concurrency: 65 | use_web_concurrency = int(web_concurrency) 66 | assert use_web_concurrency > 0, "WEB_CONCURRENCY Must be non zero" 67 | else: 68 | use_web_concurrency = max(int(default_web_concurrency), 2) 69 | if workers_max: 70 | use_web_concurrency = min(use_web_concurrency, workers_max) 71 | assert use_web_concurrency > 0, "MAX_WORKERS and WORKERS_PER_CORE Must be non zero" 72 | 73 | use_worker_class = os.getenv("WORKER_CLASS", "asyncio") 74 | assert use_worker_class in ["asyncio", "uvloop", "trio"], "WORKER_CLASS Must be asyncio, uvloop or trio" 75 | 76 | 77 | #others 78 | use_graceful_timeout = os.getenv("GRACEFUL_TIMEOUT", "120") 79 | use_errorlog = os.getenv("ERROR_LOG", "-") 80 | use_accesslog = os.getenv("ACCESS_LOG", "-") 81 | use_keepalive_timeout = os.getenv("KEEP_ALIVE", "5") 82 | 83 | 84 | #conf 85 | keep_alive_timeout = int(use_keepalive_timeout) 86 | worker_class = use_worker_class 87 | workers = use_web_concurrency 88 | loglevel = os.getenv("LOG_LEVEL", "info") 89 | accesslog = use_accesslog or None 90 | errorlog = use_errorlog or None 91 | graceful_timeout = int(use_graceful_timeout) 92 | backlog = int(os.getenv("BACKLOG", "100")) 93 | 94 | certfile = use_certfile 95 | ca_certs = use_ca_certs 96 | ciphers = use_ciphers 97 | keyfile = use_keyfile 98 | 99 | bind = use_bind 100 | if use_ssl and use_tcp: 101 | insecure_bind = use_insecure_bind 102 | if use_quic_bind: 103 | quic_bind = use_quic_bind 104 | 105 | 106 | #conf/env data 107 | conf_data = { 108 | "accesslog": accesslog, 109 | "errorlog": errorlog, 110 | "loglevel": loglevel, 111 | "backlog": backlog, 112 | "bind": bind, 113 | "insecure_bind": insecure_bind if use_tcp and use_ssl else None, 114 | "quic_bind": quic_bind if use_quic_bind else None, 115 | "graceful_timeout": graceful_timeout, 116 | "keep_alive_timeout": keep_alive_timeout, 117 | "workers": workers, 118 | "worker_class": worker_class, 119 | "env": { 120 | "host": host, 121 | "ssl_port": ssl_port if use_ssl else None, 122 | "tcp_port": tcp_port if use_tcp else None, 123 | "use_ssl": use_ssl, 124 | "use_tcp": use_tcp, 125 | "workers_multiplier": workers_multiplier, 126 | "cores": cores 127 | } 128 | } 129 | 130 | print(json.dumps(conf_data, indent = 4), flush = True) 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Deploy](https://github.com/bynect/hypercorn-fastapi/workflows/Deploy/badge.svg?branch=main)](https://github.com/bynect/hypercorn-fastapi-docker/actions?query=workflow%3ADeploy) 3 | 4 | ## Image tags and Dockerfiles 5 | 6 | * [`python3.9`, `latest`](images/python3.9.dockerfile) (**[*](#python-39-support)**) 7 | * [`python3.9-slim`](images/python3.9-slim.dockerfile) (**[*](#python-39-support)**) 8 | * [`python3.9-alpine`](images/python3.9-alpine.dockerfile) (**[*](#python-39-support)**) 9 | * [`python3.8`](images/python3.8.dockerfile) 10 | * [`python3.8-slim`](images/python3.8-slim.dockerfile) 11 | * [`python3.8-alpine`](images/python3.8-alpine.dockerfile) 12 | * [`python3.7`](images/python3.7.dockerfile) 13 | * [`python3.7-slim`](images/python3.7-slim.dockerfile) 14 | * [`python3.7-alpine`](images/python3.7-alpine.dockerfile) 15 | 16 | # **hypercorn-fastapi-docker** 17 | 18 | Docker image with [Hypercorn][hypercorn site] for [FastAPI][fastapi site] application in Python 3.7+. With slim and alpine options. 19 | 20 | * **[Github repo][github repo]** 21 | * **[Docker hub][docker repo]** 22 | 23 | ## Hypercorn 24 | **[Hypercorn][hypercorn site]** is an HTTP2 ready ASGI web server based on the sans-io hyper, h11, h2, and wsproto libraries and inspired by Gunicorn. 25 | 26 | Hypercorn supports HTTP/1, HTTP/2, WebSockets (over HTTP/1 and HTTP/2), ASGI/2, and ASGI/3 specifications. Hypercorn can utilise asyncio, uvloop, or trio worker types. 27 | 28 | ## FastAPI 29 | **[FastAPI][fastapi site]** is a modern, fast (high-performance), web framework for building APIs with Python 3.6+. 30 | 31 | The key features are: 32 | 33 | * Fast: Very high performance, on par with NodeJS and Go (thanks to Starlette and Pydantic). 34 | * Fast to code: Increase the speed to develop features by about 300% to 500% *. 35 | * Less bugs: Reduce about 40% of human (developer) induced errors. * 36 | * Intuitive: Great editor support. Completion everywhere. Less time debugging. 37 | * Easy: Designed to be easy to use and learn. Less time reading docs. 38 | * Short: Minimize code duplication. Multiple features from each parameter declaration. Less bugs. 39 | * Robust: Get production-ready code. With automatic interactive documentation. 40 | * Standards-based: Based on (and fully compatible with) the open standards for APIs: OpenAPI (previously known as Swagger) and JSON Schema. 41 | 42 | * estimation based on tests on an internal development team, building production applications. 43 | 44 | ## How to start 45 | * You can use this image as a base image for other images, using this in your Dockerfile: 46 | 47 | ```dockerfile 48 | FROM bynect/hypercorn-fastapi:python3.8-slim 49 | 50 | COPY ./app /app 51 | ``` 52 | It will expect a file either at `/app/app/main.py` and `/app/main` containing the variable `app` containing your FastAPI application. 53 | 54 | Then you can build you Dockerfile, e.g: 55 | ```sh 56 | $ docker build -t myimage ./ 57 | ``` 58 | 59 | # Usage 60 | ## Environment variables 61 | These are the environment variables that you can set in the container to configure it and their default values. 62 | You can set alternative values for them either from shell or from Dockerfile, e.g: 63 | ```sh 64 | #from shell 65 | $ docker run -d -p 80:80 -e MODULE_NAME="custom_app.custom_main" myimage 66 | ``` 67 | ```dockerfile 68 | #from Dockerile 69 | FROM bynect/hypercorn-fastapi:python3.8-slim 70 | 71 | ENV MODULE_NAME="custom_app.custom_main" 72 | 73 | COPY ./app /app 74 | ``` 75 | 76 | 77 | #### `MODULE_NAME` 78 | 79 | The Python "module" (file) to be imported by Hypercorn, this module would contain the actual application in a variable. 80 | 81 | By default: 82 | 83 | * `app.main` if there's a file `/app/app/main.py` or 84 | * `main` if there's a file `/app/main.py` 85 | 86 | For example, if your main file was at `/app/custom_app/custom_main.py`, you could set it like: 87 | 88 | ```sh 89 | $ docker run -d -p 80:80 -e MODULE_NAME="custom_app.custom_main" myimage 90 | ``` 91 | 92 | 93 | #### `VARIABLE_NAME` 94 | 95 | The variable inside of the Python module that contains the FastAPI application. 96 | 97 | By default: 98 | 99 | * `app` 100 | 101 | For example, if your main Python file has something like: 102 | 103 | ```python 104 | from fastapi import FastAPI 105 | 106 | api = FastAPI() 107 | 108 | 109 | @api.get("/") 110 | def read_root(): 111 | return {"Hello": "World"} 112 | ``` 113 | 114 | In this case `api` would be the variable with the FastAPI application. You could set it like: 115 | 116 | ```sh 117 | $ docker run -d -p 80:80 -e VARIABLE_NAME="api" myimage 118 | ``` 119 | 120 | 121 | #### `APP_MODULE` 122 | 123 | The string with the Python module and the variable name passed to Hypercorn. 124 | 125 | By default, set based on the variables `MODULE_NAME` and `VARIABLE_NAME`: 126 | 127 | * `app.main:app` or 128 | * `main:app` 129 | 130 | You can set it like: 131 | 132 | ```sh 133 | $ docker run -d -p 80:80 -e APP_MODULE="custom_app.custom_main:api" myimage 134 | ``` 135 | 136 | 137 | #### `HYPERCORN_CONF` 138 | 139 | The path to a Hypercorn Python configuration file. 140 | 141 | By default: 142 | 143 | * `/app/app/hypercorn_conf.py` if file exists 144 | * `/app/hypercorn_conf.py` if file exists 145 | * `/hypercorn_conf.py` included file 146 | 147 | * ordered by priority. 148 | 149 | You can set it like: 150 | 151 | ```sh 152 | $ docker run -d -p 80:80 -e GUNICORN_CONF="/app/custom_gunicorn_conf.py" myimage 153 | ``` 154 | 155 | **Note**: that `HYPERCORN_CONF` needs the prefix `file:` for Python file, `python:` for Python module and no prefix for TOML file. 156 | 157 | 158 | #### `WORKERS_PER_CORE` 159 | 160 | This image will check how many CPU cores are available in the current server running your container. 161 | 162 | It will set the number of workers to the number of CPU cores multiplied by this value. 163 | 164 | By default: 165 | 166 | * `1` 167 | 168 | You can set it like: 169 | 170 | ```sh 171 | $ docker run -d -p 80:80 -e WORKERS_PER_CORE="3" myimage 172 | ``` 173 | 174 | If you used the value `3` in a server with 2 CPU cores, it would run 6 worker processes. 175 | 176 | You can use floating point values too. 177 | 178 | So, for example, if you have a big server (let's say, with 8 CPU cores) running several applications, and you have a FastAPI application that you know won't need high performance. And you don't want to waste server resources. You could make it use `0.5` workers per CPU core. For example: 179 | 180 | ```sh 181 | $ docker run -d -p 80:80 -e WORKERS_PER_CORE="0.5" myimage 182 | ``` 183 | 184 | In a server with 8 CPU cores, this would make it start only 4 worker processes. 185 | 186 | 187 | #### `MAX_WORKERS` 188 | 189 | Set the maximum number of workers to use. 190 | 191 | You can use it to let the image compute the number of workers automatically but making sure it's limited to a maximum. 192 | 193 | This can be useful, for example, if each worker uses a database connection and your database has a maximum limit of open connections. 194 | 195 | By default it's not set, meaning that it's unlimited. 196 | 197 | You can set it like: 198 | 199 | ```sh 200 | $ docker run -d -p 80:80 -e MAX_WORKERS="24" myimage 201 | ``` 202 | 203 | This would make the image start at most 24 workers, independent of how many CPU cores are available in the server. 204 | 205 | 206 | #### `WEB_CONCURRENCY` 207 | 208 | Override the automatic definition of number of workers. 209 | 210 | By default: 211 | 212 | * Set to the number of CPU cores in the current server multiplied by the environment variable `WORKERS_PER_CORE`. So, in a server with 2 cores, by default it will be set to `2`. 213 | 214 | You can set it like: 215 | 216 | ```sh 217 | $ docker run -d -p 80:80 -e WEB_CONCURRENCY="2" myimage 218 | ``` 219 | 220 | This would make the image start 2 worker processes, independent of how many CPU cores are available in the server. 221 | 222 | 223 | #### `HOST` 224 | 225 | The "host" used by Hypercorn, the IP where Hypercorn will listen for requests. 226 | 227 | It is the host inside of the container. 228 | 229 | So, for example, if you set this variable to `127.0.0.1`, it will only be available inside the container, not in the host running it. 230 | 231 | It's is provided for completeness, but you probably shouldn't change it. 232 | 233 | By default: 234 | 235 | * `0.0.0.0` 236 | 237 | 238 | #### `TCP_PORT` 239 | 240 | The tcp port the container should listen on when `USE_TCP` is set to true. 241 | 242 | If you are running your container in a restrictive environment that forces you to use some specific port (like `8080`) you can set it with this variable. 243 | 244 | By default: 245 | 246 | * `80` 247 | 248 | You can set it like: 249 | 250 | ```sh 251 | $ docker run -d -p 80:8080 -e TCP_PORT="8080" myimage 252 | ``` 253 | 254 | 255 | #### `USE_SSL` 256 | 257 | If Hypercorn will use ssl-related options. When false ssl-related options are not used. 258 | 259 | By default is set to: 260 | * `false` 261 | 262 | >Depends on `CA_CERTS` - `CERTFILE` - `KEYFILE` 263 | >At least one of `USE_SSL` and `USE_TCP` **MUST** be set to true. 264 | 265 | 266 | #### `USE_TCP` 267 | 268 | If Hypercorn will use tcp-related options. When false tcp-related options are not used. 269 | 270 | By default is set to: 271 | * `true` 272 | 273 | >At least one of `USE_SSL` and `USE_TCP` **MUST** be set to true. 274 | 275 | 276 | #### `SSL_PORT` 277 | 278 | The ssl port the container should listen on when `USE_SSL` is set to true. 279 | 280 | If you are running your container in a restrictive environment that forces you to use some specific port (like `8000`) you can set it with this variable. 281 | 282 | By default: 283 | 284 | * `443` 285 | 286 | You can set it like: 287 | 288 | ```sh 289 | $ docker run -d -p 443:8000 -e SSL_PORT="8000" myimage 290 | ``` 291 | >Depens on `USE_SSL` 292 | 293 | 294 | #### `BIND` 295 | 296 | The actual host and port passed to Hypercorn. 297 | 298 | If `USE_SSL` is set to true the default value will be based on `HOST` and `SSL_PORT`. 299 | So, if you didn't change anything, it will be set by default to: 300 | 301 | * `0.0.0.0:443` 302 | 303 | Otherwise, if `USE_SSL` is not set to true, the value will be based on `HOST` and `TCP_PORT`. 304 | So, if you didn't change anything, it will be set by default to: 305 | 306 | * `0.0.0.0:80` 307 | 308 | You can set it like: 309 | 310 | ```sh 311 | $ docker run -d -p 80:8080 -e BIND="0.0.0.0:8080" myimage 312 | ``` 313 | 314 | 315 | #### `INSECURE_BIND` 316 | 317 | The host and port passed to Hypercorn as fallback in HTTPS connections. 318 | 319 | If `USE_SSL` and `USE_TCP` are both true the default value is based on the variables `HOST` and `TCP_PORT`. 320 | 321 | So, if you didn't change anything, it will be set by default to: 322 | 323 | * `0.0.0.0:80` 324 | 325 | Otherwise, if `USE_SSL` is not set to true or `USE_TCP` is set to false, the value will be set to `None`. 326 | 327 | You can manually set only when the aforementioned conditions are true. 328 | >Depens on `USE_SSL` and `USE_TCP` 329 | 330 | #### `QUIC_BIND` 331 | 332 | Quic bind to be used instead of bind. By default it's not set. 333 | 334 | You can set it like: 335 | 336 | ```sh 337 | $ docker run -d -p 80:8080 -e QUIC_BIND="0.0.0.0:8080" myimage 338 | ``` 339 | 340 | 341 | #### `LOG_LEVEL` 342 | 343 | The log level for Hypercorn. 344 | 345 | One of: 346 | 347 | * `debug` 348 | * `info` 349 | * `warning` 350 | * `error` 351 | * `critical` 352 | 353 | By default, set to `info`. 354 | 355 | If you need to squeeze more performance sacrificing logging, set it to `warning`, for example: 356 | 357 | You can set it like: 358 | 359 | ```sh 360 | $ docker run -d -p 80:8080 -e LOG_LEVEL="warning" myimage 361 | ``` 362 | 363 | #### `WORKER_CLASS` 364 | 365 | The worker class to be used by Hypercorn. 366 | 367 | By default, set to `asyncio`. 368 | 369 | The three avaible values are: 370 | * `asyncio` 371 | * `uvloop` 372 | * `trio` 373 | 374 | You can set it like: 375 | 376 | ```sh 377 | $ docker run -d -p 80:8080 -e WORKER_CLASS="uvloop" myimage 378 | ``` 379 | 380 | 381 | #### `CA_CERTS` 382 | 383 | Path to CA certificate file. By default it's not set. 384 | 385 | >Depends on `USE_SSL` 386 | 387 | 388 | #### `CERTFILE` 389 | 390 | Path to CA certificate file. By default it's not set. 391 | 392 | >Depends on `USE_SSL` 393 | 394 | 395 | #### `KEYFILE` 396 | 397 | Path to CA certificate file. By default it's not set. 398 | 399 | >Depends on `USE_SSL` 400 | 401 | 402 | #### `CIPHERS` 403 | 404 | Ciphers used by ssl connection. By default: 405 | * `"ECDHE+AESGCM"` 406 | 407 | >Depends on `USE_SSL` 408 | 409 | 410 | #### `KEEP_ALIVE` 411 | 412 | The number of seconds to wait for requests on a Keep-Alive connection. 413 | 414 | By default, set to `5`. 415 | 416 | You can set it like: 417 | 418 | ```sh 419 | $ docker run -d -p 80:8080 -e KEEP_ALIVE="20" myimage 420 | ``` 421 | 422 | 423 | #### `GRACEFUL_TIMEOUT` 424 | 425 | Timeout for graceful workers restart. 426 | 427 | By default, set to `120`. 428 | 429 | You can set it like: 430 | 431 | ```sh 432 | $ docker run -d -p 80:8080 -e GRACEFUL_TIMEOUT="20" myimage 433 | ``` 434 | 435 | 436 | #### `ACCESS_LOG` 437 | 438 | The access log file to write to. 439 | 440 | By default `"-"`, which means stdout (print in the Docker logs). 441 | 442 | If you want to disable `ACCESS_LOG`, set it to an empty value. 443 | 444 | For example, you could disable it with: 445 | 446 | ```sh 447 | $ docker run -d -p 80:8080 -e ACCESS_LOG= myimage 448 | ``` 449 | 450 | 451 | #### `ERROR_LOG` 452 | 453 | The error log file to write to. 454 | 455 | By default `"-"`, which means stderr (print in the Docker logs). 456 | 457 | If you want to disable `ERROR_LOG`, set it to an empty value. 458 | 459 | For example, you could disable it with: 460 | 461 | ```sh 462 | $ docker run -d -p 80:8080 -e ERROR_LOG= myimage 463 | ``` 464 | 465 | 466 | #### `BACKLOG` 467 | 468 | The maximum number of pending connections. By default set to `100`. 469 | 470 | 471 | #### `PRE_START_PATH` 472 | 473 | The path where to find the pre-start script. 474 | 475 | By default, set to `/app/prestart.sh`. 476 | 477 | You can set it like: 478 | 479 | ```sh 480 | $ docker run -d -p 80:8080 -e PRE_START_PATH="/custom/script.sh" myimage 481 | ``` 482 | 483 | 484 | ## Prestart script 485 | If you need to run anything before starting the app, you can add a file prestart.sh to the directory `/app`. 486 | The image will automatically detect and run it before starting everything. 487 | If you need to run a Python script before starting the app, you could make the /app/prestart.sh file run your Python script, with something like: 488 | 489 | ```sh 490 | #! /usr/bin/env bash 491 | 492 | # Run custom Python script before starting 493 | python /app/my_custom_prestart_script.py 494 | ``` 495 | You can customize the location of the prestart script with the environment variable `PRE_START_PATH` described above. 496 | 497 | 498 | ## Hypercorn configuration 499 | 500 | The image includes a default Gunicorn Python config file at /gunicorn_conf.py. 501 | It uses the environment variables declared above to set all the configurations. 502 | 503 | You can override it by including a file in: 504 | 505 | * `/app/app/hypercorn_conf.py` 506 | * `/app/hypercorn_conf.py` 507 | * `/hypercorn_conf.py` 508 | 509 | * ordered by priority. 510 | 511 | ## Development live reload 512 | The default program that is run is at `/start.sh`. It does everything described above. 513 | 514 | There's also a version for development with live auto-reload at: 515 | 516 | `/start-reload.sh` 517 | #### Details 518 | For development, it's useful to be able to mount the contents of the application code inside of the container as a Docker "host volume", to be able to change the code and test it live, without having to build the image every time. 519 | 520 | In that case, it's also useful to run the server with live auto-reload, so that it re-starts automatically at every code change. 521 | 522 | The additional script `/start-reload.sh` runs Hypercorn with 1 `asyncio` worker. 523 | 524 | It is ideal for development. 525 | 526 | #### Usage 527 | For example, instead of running: 528 | 529 | ```sh 530 | $ docker run -d -p 80:80 myimage 531 | ``` 532 | You could run: 533 | ```sh 534 | $ docker run -d -p 80:80 -v $(pwd):/app myimage /start-reload.sh 535 | ``` 536 | * `-v $(pwd):/app`: means that the directory `$(pwd)` should be mounted as a volume inside of the container at `/app`. 537 | * `$(pwd)`: runs pwd ("print working directory") and puts it as part of the string. 538 | * `/start-reload.sh`: adding something (like `/start-reload.sh`) at the end of the command, replaces the default "command" with this one. In this case, it replaces the default (`/start.sh`) with the development alternative `/start-reload.sh`. 539 | 540 | #### Development live reload - Technical Details 541 | As `/start-reload.sh` runs Hypercorn for debug/development purpose it doesn't use hypercorn_config file. 542 | 543 | But these environment variables will work the same as described above: 544 | 545 | * `MODULE_NAME` 546 | * `VARIABLE_NAME` 547 | * `APP_MODULE` 548 | * `HOST` 549 | * `TCP_PORT` (only tcp avaible) 550 | * `LOG_LEVEL` 551 | 552 | 553 | ### Falsy/Truly value 554 | 555 | The included `/hypercorn_conf.py` has some options that accepts boolean value. 556 | These are the valid values. Invalid values will raise an exception. 557 | 558 | Falsy values (compared after lowered): 559 | * `"no"` 560 | * `"n"` 561 | * `"0"` 562 | * `"false"` 563 | 564 | Truly values (compared after lowered): 565 | * `"yes"` 566 | * `"y"` 567 | * `"1"` 568 | * `"true"` 569 | 570 | 571 | ## Python 3.9 support 572 | 573 | Python 3.9 is now supported, but some optional packages are not installed due to incompatible Python version. 574 | 575 | Incompatible packages: 576 | * `trio` (`hypercorn[trio]`) 577 | 578 | ## License 579 | Licensed under MIT License. 580 | 581 | Based on [tiangolo/uvicorn-gunicorn-docker](https://github.com/tiangolo/uvicorn-gunicorn-docker) 582 | 583 | [docker tags]: https://hub.docker.com/repository/docker/bynect/hypercorn-fastapi/tags 584 | [docker repo]: https://hub.docker.com/repository/docker/bynect/hypercorn-fastapi 585 | [github repo]: https://github.com/bynect/hypercorn-fastapi-docker 586 | [fastapi site]: https://fastapi.tiangolo.com/ 587 | [hypercorn site]: https://pgjones.gitlab.io/hypercorn/ 588 | --------------------------------------------------------------------------------