How to expose a FastAPI backend service using a public url

crazytechprojectsHOBBY

9 months ago

I have two projects. In the first project I have 2 services: a backend service (FastAPI) and Frontend service (NextJS), the frontend service is exposed and is talking to the backend using the private network. and everything is working fine.I created another project and I added the backend service only (using the exact github repo - everything same as the first project), but the only difference is that i want to expose this backend using a public url and be able to make a request from my personal computer. When I deploy it there is no error and the logs show the service is up and running:`Uvicorn running on http://[::]:8000 (Press CTRL+C to quit)`I have also added this env variable: PORT:8000 in the Railway service configuration. Here is my docker file:

CMD ["uvicorn", "main:app", "--host", "::", "--port", "8000"]

here is my docker-compose file:

fastapi-app:
    container_name: fastapi-app
    build: .
    ports:
      - "8000:8000"
    volumes:
      - ./app:/home/app
    environment:
      ENV_NAME: ${ENV_NAME}
    networks:
      - mainnetwork
    command: ["uvicorn", "main:app", "--host", "::", "--port", "8000", "--reload"]

and when I click generate public url, a url is generated, but when I click on it, it shows: Application failed to respond

This error appears to be caused by the application.and I get 502 status code error. So everything is working, except I am not able to reach to the backend using the public url. How to be able to access this backend using the public url?Many thanks in advance.

14 Replies

crazytechprojectsHOBBY

9 months ago

long story short, the backend service is running, if i deploy a frontend service then it can talk to the backend using the private network and the response coming as expected, so backend is responding through the private network, but when i click on generate public url and i try to expose it, i get 502 error application is not responding.


9 months ago

Use this as your CMD line -

CMD uvicorn main:app --host 0.0.0.0 --port $PORT

This enables public access but removes access from the private network, I would recommend you use hypercorn so that you can dual stack bind to IPv4 for the public network, and IPv6 for the private network.


9 months ago

0.0.0.0 as suggested by Brody should work. Turns out :: on uvicorn doesn't listen on IPv4 by default [0] and we translate addresses from v6->v4 over our internal routing, so traffic arriving from our proxy will hit your app as IPv4.

[0] https://github.com/encode/uvicorn/discussions/1529


crazytechprojectsHOBBY

9 months ago

@brody @rc Thank you so much for your help. I updated my dockerfile to be:

CMD ["hypercorn", "main:app", "--bind", "::1:8000", "--bind", "0.0.0.0:8000"]

when i deploy it I can see these logs:
[2024-07-25 02:53:16 +0000] [9] [INFO] Running on http://[::1]:8000 (CTRL + C to quit)

Jul 24 21:53:16

[2024-07-25.02:53:16] [INFO] [info] Running on http://[::1]:8000 (CTRL + C to quit)

Jul 24 21:53:16

[2024-07-25 02:53:16 +0000] [9] [INFO] Running on http://0.0.0.0:8000 (CTRL + C to quit)

Jul 24 21:53:16

[2024-07-25.02:53:16] [INFO] [info] Running on http://0.0.0.0:8000 (CTRL + C to quit)

I have also ran it locally and tested it using curl for both ipv4 and ipv6 and it worked for both. But when deploying it to railway, still the same issues, I still get: "Application failed to respond" with status code 502.


9 months ago

Use this instead -

CMD hypercorn main:app --bind [::]:${PORT}

crazytechprojectsHOBBY

9 months ago

ahhh, hate when this happened, turned out im on the prod env and did not copy the port env var, so now it is working, silly error :D.
Thank you so much for the help!!


9 months ago

PORT will be randomly generated at runtime, if you wish to use this service with the private network use the same CMD line and simply set a fixed PORT service variable.


crazytechprojectsHOBBY

9 months ago

Oh wow, since you already mentioned that, my frontend was able to talk to my backend and call the apis, but now Im getting this:

Mixed Content: The page at 'https://dev.example.com/signup' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://example.railway.internal:8000/signup'. This request has been blocked; the content must be served over HTTPS.


9 months ago

A client side rendered frontend app needs to call the public domain of the backend, it wouldn't be a very private network if anyone's browser could call your private domain.


crazytechprojectsHOBBY

9 months ago

Yeah, make sense. Im using NextJS app. Im not a frontend guy so not really sure if the backend endpoint should be public or not. However, I was thinking to keep the frontend talking to the backend using the private network, while i use the backend exposed url for other purposes (such as login using Google callback for authentication).


9 months ago

The frontend would need to be server side rendered to talk to the backend via the private network.


chinocejasHOBBY

4 months ago

Hi, I'm getting the same issue with fastapi app. (https://limitbreak-production.up.railway.app/)

I tried using hypercorn, setting port env to 8000/5000, also I tried with hard coded values in docker file.

If I run docker locally is working well, the problem is when I deploy to railway, it finish successfully but when I request from Postman to the api I got a 502 status response.

I don't know what else I should try.

those are logs from Deploy Logs tab:
You reached the start of the range → Dec 27, 2024 7:09 PM

Dec 27 19:10:10

Starting Container

this is my dockerfile:

# Use Python base image
FROM python:3.12-slim

# Set environment variables
# Prevent Python from writing .pyc files
ENV PYTHONDONTWRITEBYTECODE=1  
# Prevent buffering of stdout and stderr
ENV PYTHONUNBUFFERED=1        
ENV PYTHONPATH=/app  

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE ${PORT}

CMD ["sh", "-c", "hypercorn main:app --bind ::1:${PORT} --bind 0.0.0.0:${PORT}"]

4 months ago

chinocejas, please see our docs on this topic -

https://docs.railway.com/guides/private-networking#python--hypercorn

tl;dr - you need to use :: not ::1


brody

chinocejas, please see our docs on this topic -https://docs.railway.com/guides/private-networking#python--hypercorntl;dr - you need to use :: not ::1

chinocejasHOBBY

4 months ago

thanks for answer brody.

I tied with those options and I still getting same issue (502 Status response)

CMD ["sh", "-c", "hypercorn main:app --bind [::]:${PORT-8000}"]
CMD ["sh", "-c", "hypercorn main:app --bind [::]:${PORT-8000} --bind 0.0.0.0:${PORT-8000}"]