The Problem
I am deploying a Docker image running FastAPI to Heroku using the Docker Container Registry deployment method. When I deploy the container I get the following logs when I run heroku logs --app <app-name> --tail
2024-06-03T20:57:32.222808+00:00 app[api]: Release v29 created by user [email protected]
2024-06-03T20:57:32.222808+00:00 app[api]: Deployed web (228f5e77d4e2) by user [email protected]
2024-06-03T20:57:45.637479+00:00 heroku[web.1]: Starting process with command `/bin/sh -c "./start.sh ${PORT}"`
2024-06-03T20:57:46.235100+00:00 heroku[web.1]: Process exited with status 127
2024-06-03T20:57:46.185240+00:00 app[web.1]: /bin/sh: 1: ./start.sh 53182: not found
2024-06-03T20:57:46.255899+00:00 heroku[web.1]: State changed from starting to crashed
In the Heroku UI I can see the command for starting a web
process is web /bin/sh -c "./start.sh ${PORT}"
which is correct as that command works locally. I’ve even exec’d into the container and run /bin/sh -c "./start.sh <port>"
to verify it will work, and it does (meaning I can hit any and all of my API endpoints and get the expected results).
The Details
Dockerfile
FROM node:21 as ui-build
ENV PORT $PORT
WORKDIR /opt/ui
COPY ui .
RUN npm ci && npm run build
FROM python:3.11 as final-build
WORKDIR /opt/app
COPY --from=ui-build /opt/ui/build ./ui/build
COPY server server
RUN cd server && pip install -r requirements.txt
# Apparently Heroku doesn't respect the EXPOSE directive.
EXPOSE $PORT
WORKDIR /opt/app/server
COPY ./start.sh .
# Not sure why these commands are not working but the start.sh script seems to be going in the right direction.
# ENTRYPOINT [ "uvicorn", "api.app:app" ]
# CMD [ "--host", "0.0.0.0", "--port", $PORT ]
CMD "./start.sh ${PORT}"
I’m using the gonuit/heroku-docker-deploy Github Action to build and push the docker image to Heroku’s container registry. Here is how I use the action in my workflow.
name: Build and Push Image
on:
push:
branches:
- main
jobs:
build-image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and Push to Heroku
uses: gonuit/[email protected]
with:
email: [email protected]
heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
heroku_app_name: <app-name>
The Links
These are some of the links I’ve used to come up with code I currently have but neither has worked for me.
- https://akshaykhatale.medium.com/deploy-fastapi-on-heroku-using-docker-container-a920f839de9b
- How to deploy fast api as a backend in docker container to heroku
This final link is the official Heroku Docs on deploying w/ Docker Container Registry which to the best of my knowledge I’ve implemented identically.
The Solution
An ideal solution for me would be able to deploy my application using Docker, and Github Actions. If I need to add a Procfile or Heroku.yml file to the application I’m alright with that it just seems to be something that’s not needed for the deployment method I’m using.
2
Answers
My answer is basically a re-skin of this answer. https://stackoverflow.com/a/67747986/1464160
The correct way to define the
CMD
directive isOf course the path
api.app:app
and the default port # can be changed to whatever you want it to be. The important part seems to be having"sh", "-c"
at the beginning of theCMD
directive.Perhaps try changing CMD to:
CMD ["/path/to/start.sh", "--port ${PORT}"]