I am running Hypercorn with --reload
inside a Docker container. The file I am running is kept in a volume managed by Docker Compose.
When I change the file on my system, I can see that the change is reflected in the volume, e.g. with docker compose exec myapp /bin/cat /app/runtime/service.py
.
However, when I change a file in this way, Hypercorn does not restart as I would have expected. Is there some adverse interaction between Hypercorn and the Docker volume? Or am I expecting something from the --reload
option that I should not expect?
Example files are below. My expectation was that modifying runtime/service.py
from outside the container would trigger Hypercorn to restart the server with the modified version of the file. But this does not occur.
Edit: I should add that I am using Docker 20.10.5 via Docker Desktop for Mac, on MacOS 10.14.6.
Edit 2: This might be a Hypercorn bug. If I add uvicorn[standard]
in requirements.txt
and run python -m uvicorn --reload --host 0.0.0.0 --port 8001 service:app
, the reloading works fine. Possibly related: https://gitlab.com/pgjones/hypercorn/-/issues/185
entrypoint.sh
:
#!/bin/sh
cd /app/runtime
/opt/venv/bin/python -m hypercorn --reload --bind 0.0.0.0:8001 service:app
Dockerfile
:
FROM $REDACTED
RUN /opt/venv/bin/python -m pip install -U pip
RUN /opt/venv/bin/pip install -U setuptools wheel
COPY requirements.txt /app/requirements.txt
RUN /opt/venv/bin/pip install -r /app/requirements.txt
COPY requirements-dev.txt /app/requirements-dev.txt
RUN /opt/venv/bin/pip install -r /app/requirements-dev.txt
COPY entrypoint.sh /app/entrypoint.sh
EXPOSE 8001/tcp
CMD ["/app/entrypoint.sh"]
docker-compose.yml
:
version: "3.8"
services:
api:
container_name: api
hostname: myapp
build:
context: .
ports:
- 8001:8001
volumes:
- ./runtime:/app/runtime
runtime/service.py
:
import logging
import quart
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
app = quart.Quart(__name__)
@app.route('/')
async def handle_hello():
logger.info('Handling request.')
return 'Hello, world!\n'
@app.route('/bad')
async def handle_bad():
logger.critical('Bad request.........')
raise RuntimeError('Oh no!!!')
Here is a minimal, fully working example which does auto-reload using hypercorn
:
docker-compose.yaml
services:
app:
build: .
# Here --reload is used which works as intended!
command: hypercorn --bind 0.0.0.0:8080 --reload src:app
ports:
- 8080:8080
volumes:
- ./src:/app/src
Dockerfile
FROM python:3.10-slim-bullseye
WORKDIR /app
RUN pip install hypercorn==0.14.3 quart==0.18.0
COPY src ./src
EXPOSE 8080
ENV QUART_APP=src:app
# This is the production command; docker-compose.yaml overwrites it for local development
CMD hypercorn --bind 0.0.0.0:8080 src:app
src/__init__.py
from quart import Quart
app=Quart(__name__)
@app.route('/', methods=['GET'])
def get_root():
return "Hello world!"
Run via docker-compose up
and notice how hypercorn
reloads once __init__.py
got modified.