<p>I have written an application that runs a FastAPI server inside a Kubernetes pod. The external communication with the pod goes through an nginx ingress controller in a separate pod. I am running nginx:1.17.0.</p>

<p>When it is all up and running I can use <code>curl</code> calls to interact with the app server through the ingress address, and access all the simple GET paths as well as <em>address/openapi.json</em> in my browser. I can also access the interactive documentation page if I use the internal ip of the app service in Kubernetes.
However trying to reach the interactive documentation page (<em>address/docs#/default/</em>) gives me an error regarding <em>/openapi.json</em>.</p>

<p><a href="https://i.stack.imgur.com/yKAT6.png" rel="noreferrer"><img src="https://i.stack.imgur.com/yKAT6.png" alt="enter image description here"></a></p>

<p>Since the <code>curl</code> calls work as expected I do not think the problem is necessarily in the ingress definition but as using the internal ip of the app also works fine the issue should not be inside the app.<br>
I have included the ingress definition file below.</p>

<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.0
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80

---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
  - host: my-host.info
    http:
      paths:
      - path: /server(/|$)(.*)
        backend:
          serviceName: my-app-service # This is the service that runs my fastAPI server pod
          servicePort: 80
</code></pre>

<p><strong>EDIT</strong><br>
This is the service.yaml file</p>

<pre><code>apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  type: ClusterIP
  selector:
    app: server
  ports:
    - protocol: "TCP"
      port: 80
      targetPort: 80
</code></pre>

<p>As the service is a ClusterIP inside my local cluster I have might be able to curl straight to it, I have not tried though. When I curl I use commands like  </p>

<pre><code>curl -X GET "http://my-host.info/server/subpath/" -H "accept: application/json"
curl -X POST "http://my-host.info/server/subpath/update/" -H "accept: application/json"
</code></pre>

<p>from outside the local cluster.</p>

<p>These are all the services that are running:   </p>

<pre><code>NAMESPACE              NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                  AGE
default                kubernetes                  ClusterIP   10.96.0.1       &lt;none&gt;        443/TCP                  11d
default                my-app-service              ClusterIP   10.96.68.29     &lt;none&gt;        80/TCP                   18h
kube-system            kube-dns                    ClusterIP   10.96.0.10      &lt;none&gt;        53/UDP,53/TCP,9153/TCP   28d
kubernetes-dashboard   dashboard-metrics-scraper   ClusterIP   10.96.114.1     &lt;none&gt;        8000/TCP                 28d
kubernetes-dashboard   kubernetes-dashboard        ClusterIP   10.96.249.255   &lt;none&gt;        80/TCP                   28d
</code></pre>

<p>and inside my <code>/etc/hosts</code> file I have connected 10.0.0.1 (cluster "external" IP) to <em>my-host.info</em>.</p>

<p>Any ideas of why this is happening?</p>

<p>I think you definitely should look into official documentation of FastApi: <a href="https://fastapi.tiangolo.com/advanced/behind-a-proxy/" rel="noreferrer">https://fastapi.tiangolo.com/advanced/behind-a-proxy/</a></p>
<p>As you mentioned, when accessing your app internally the Swagger auto docs works fine but when accessing from outside your cluster you've got an error regarding /openapi.json.</p>
<p>In your <code>service.yaml</code> you have:</p>
<pre><code>      - path: /server(/|$)(.*)
        backend:
          serviceName: my-app-service # This is the service that runs my fastAPI server pod
          servicePort: 80
</code></pre>
<p>and when starting your application with uvicorn you should pass the <code>root_path</code></p>
<p><code>uvicorn main:app --root-path /server</code></p>
<p>ATTENTION: Here you will be able to access the routers endpoints but no the Swagger docs. In order to get Swagger docs you have to edit your main <code>main.py</code> file:</p>
<pre class="lang-py prettyprint-override"><code>
from fastapi import FastAPI, Request

app = FastAPI(openapi_prefix=&quot;/server&quot;)

@app.get(&quot;/&quot;)
def read_root(request: Request):
    return {&quot;message&quot;: &quot;Hello World&quot;, &quot;root_path&quot;: request.scope.get(&quot;root_path&quot;)}

</code></pre>
<p>I searched why we need explicitly pass the OpenApi prefix but I found only workarounds like: <a href="https://github.com/iwpnd/fastapi-aws-lambda-example/issues/2" rel="noreferrer">https://github.com/iwpnd/fastapi-aws-lambda-example/issues/2</a></p>
<p>So I suggest to store <code>root_path</code> in environment variable <code>$ROOT_PATH=/server</code> on your system and pass it to <code>uvicorn main:app --root-path $ROOT_PATH</code> as well as in <code>main.py</code>:</p>
<pre class="lang-py prettyprint-override"><code>import os
from fastapi import FastAPI, Request

app = FastAPI(openapi_prefix=os.getenv('ROOT_PATH', ''))

@app.get(&quot;/&quot;)
def read_root(request: Request):
    return {&quot;message&quot;: &quot;Hello World&quot;, &quot;root_path&quot;: request.scope.get(&quot;root_path&quot;)}

</code></pre>
<p>UPDATE 07.07.2020</p>
<p>Currently tiangolo ready to use docker images <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" rel="noreferrer">https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker</a> are &quot;outdated&quot; if goes on fastapi version which currently is: 0.55.1 - reported here: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/issues/53" rel="noreferrer">link</a></p>
<p>&quot;root_path&quot; is supported since 0.56.0</p>
