I want to host a small web app on the Heroku free tier. I'm using docker compose
to run the frontend, backend api, and a postgres DB locally. I am following the Heroku docs on deploying an existing docker image. However, when I try access the API docs, or try curl the API (both of which work locally), I get the error
2021-01-31T15:19:23.679917+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/docs" host=apitest48398.herokuapp.com request_id=62ffd495-5977-4132-9ec7-d89abf5b1d8f fwd="77.100.21.140" dyno= connect= service= status=503 bytes= protocol=https
2021-01-31T15:19:24.465326+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/favicon.ico" host=apitest48398.herokuapp.com request_id=016de415-dc8e-4b0a-894e-8fad37ff7fee fwd="77.100.21.140" dyno= connect= service= status=503 bytes= protocol=https
Note I am deploying via the container registry, not by connecting my git repo to Heroku, hence neither a Procfile
, nor a heroku.yml
file.
Here are the steps to reproduce this behaviour in a minimal example. Note that in the real example, I have multiple docker files, so even though this simple example could have been deployed using heroku container:push
, I prefer to build the images myself so the simple example is more representative of the real use case:
fastAPI
in a file called main.py
:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
requirements.txt
file:
fastapi
uvicorn
Dockerfile
:
FROM python:3.7
ENV PORT=$PORT
COPY ./requirements.txt .
RUN pip install -r requirements.txt
COPY ./main.py .
EXPOSE $PORT
CMD uvicorn main:app --host 0.0.0.0 --port $PORT
docker build . -t herokubackendtest
heroku container:login
export APP_NAME=apitest48398
heroku create $APP_NAME
docker tag herokubackendtest registry.heroku.com/$APP_NAME/api
docker push registry.heroku.com/$APP_NAME/api
uvicorn
is running:
heroku container:release --app $APP_NAME api
heroku ps:scale --app $APP_NAME api=1
heroku ps --app $APP_NAME # should show === api (Free): /bin/sh -c uvicorn\ main:app\ --host\ 0.0.0.0\ --port\ \$PORT (1)
heroku logs --app $APP_NAME --tail
You should see the following in the logs (the port number will vary)
2021-01-31T15:17:39.806481+00:00 heroku[api.1]: Starting process with command `/bin/sh -c uvicorn\ main:app\ --host\ 0.0.0.0\ --port\ \16384`
2021-01-31T15:17:40.465132+00:00 heroku[api.1]: State changed from starting to up
2021-01-31T15:17:41.982857+00:00 app[api.1]: INFO: Started server process [5]
2021-01-31T15:17:41.982918+00:00 app[api.1]: INFO: Waiting for application startup.
2021-01-31T15:17:41.983162+00:00 app[api.1]: INFO: Application startup complete.
2021-01-31T15:17:41.983529+00:00 app[api.1]: INFO: Uvicorn running on http://0.0.0.0:16384 (Press CTRL+C to quit)
Now to test it, you should be able to see the openAPI (swagger) docs at https://apitest48398.herokuapp.com/docs
where apitest48398
is your $APP_NAME
. But instead I get the error in the logs I posted at the start of this question, and I see a page that says:
Application error
An error occurred in the application and your page could not be served. If you are the application owner, check your logs for details. You can do this from the Heroku CLI with the command
heroku logs --tail
in my browser.
I also would expect curl https://apitest48398.herokuapp.com/
to return my hello world
json, but it instead returns some HTML with an error. I've also tried with ports 443
, 80
, and the port uvicorn is running on inside the container (this one just hangs until timeout).
I assume I have missed some step, maybe with SSL? Although I understood that this was automatically enabled for any herokuapp.com domains on the free tier, and I can't see any way to pass the cert and pem files through to uvicorn.
Can anyone point me in the right direction as to how to resolve this?
Tag the image using the web
process type, as indicated by Heroku Docker Registry. In your case
docker tag herokubackendtest registry.heroku.com/$APP_NAME/web
docker push registry.heroku.com/$APP_NAME/web
heroku container:release web --app $APP_NAME