できない.dev

nginx「host not found in upstream」の原因と直し方

host not found in upstream は、転送先の名前を nginx から引けるようにすれば直る。
コンテナなら同じユーザー定義ネットワークに置く。
起動順に左右されたくなければ、resolver と変数で実行時に解決させる。

公開:

要約

nginx: [emerg] host not found in upstream "app" は、proxy_pass や upstream に書いた名前を nginx が引けないと出る。
名前は設定を読み込むときに引かれるので、引けなければ nginx は起動せず、終了コード 1 で止まる。転送先と同じネットワークに nginx を置いて、名前を引ける状態にすれば直る。

nginx: [emerg] host not found in upstream "app" in /etc/nginx/conf.d/default.conf:1
nginx: configuration file /etc/nginx/nginx.conf test failed

引用符の中身は、名前を書いた場所で少し変わる。
nginx 1.29.8 では次のように出た。

書いた場所表示
proxy_pass http://app:8000;host not found in upstream "app"
upstream の server app:8000;host not found in upstream "app:8000"

解決策

1. 同じネットワークに置く

ユーザー定義ネットワークを作り、転送先と nginx の両方を入れる。proxy_pass にはコンテナ名を書く。

docker network create web
docker run -d --name app --network web python:3.14-slim python -m http.server 8000
docker run -d --name nginx --network web -p 8080:80 \
  -v "$PWD/default.conf:/etc/nginx/conf.d/default.conf:ro" nginx:1.29

起動する前に確かめるなら、同じネットワークで nginx -t を打つ。

docker run --rm --network web \
  -v "$PWD/default.conf:/etc/nginx/conf.d/default.conf:ro" nginx:1.29 nginx -t

2. Compose では depends_on を書く

同じ compose.yaml のサービスは同じネットワークに入るので、残るのは起動順だけである。depends_on を足すと、3 回とも nginx は起動した。

services:
  web:
    image: nginx:1.29
    depends_on: [app]
    volumes:
      - ./default.conf:/etc/nginx/conf.d/default.conf:ro
  app:
    image: python:3.14-slim
    command: python -m http.server 8000

3. resolver と変数で実行時に解決する

転送先が止まっていても nginx を起動したいときは、名前を変数に入れて、リクエストのたびに引かせる。127.0.0.11 は Docker の組み込み DNS(新しいタブで開く) のアドレスである。

server {
    listen 80;
    resolver 127.0.0.11 valid=10s;
    set $upstream http://app:8000;
 
    location / {
        proxy_pass $upstream;
    }
}

転送先が無い間、nginx は動いたまま 502 を返し、error.log に app could not be resolved (3: Host not found) と書く。
転送先を起動すると、reload しなくても 200 に変わった。

resolver か変数の片方だけでは足りない。resolver だけ書いて proxy_pass に名前を直接書くと、同じ [emerg] で止まる。
変数だけだと、リクエストのたびに no resolver defined to resolve app で 502 になる。

変数にすると、location のパスの置き換えも変わる。location /b/ に proxy_pass $upstream/; と書くと、/b/x への要求も / へ転送された。
パスを削るなら rewrite を足す。

location /d/ {
    set $upstream http://app:8000;
    rewrite ^/d/(.*)$ /$1 break;
    proxy_pass $upstream;
}

4. nginx 1.27.3 以降なら upstream の resolve を使う

upstream の server に resolve を付けると、起動時に名前を引けなくても止まらず、あとから解決される。zone を書かないと、resolving names at run time requires upstream "backend" で止まる。

resolver 127.0.0.11 valid=5s;
 
upstream backend {
    zone backend 64k;
    server app:8000 resolve;
}

nginx 1.29.8 では、転送先が無い間は 502 を返し、起動すると 200 に変わった。
1.26 と 1.27.2 では invalid parameter "resolve" で止まり、1.27.3 では nginx -t が通った。

よくある原因

  1. 転送先のコンテナが別のネットワークにいる。
    Docker の名前解決は同じネットワークの中でしか効かないので、転送先が動いていてもこのエラーになる。
  2. 既定の bridge ネットワークでコンテナ名を書いている。--network を付けずに docker run したコンテナどうしは、同じ bridge にいても名前を引けない(新しいタブで開く)。
  3. 転送先のコンテナがまだ起動していない。depends_on の無い compose.yaml を 3 回 docker compose up すると、2 回は nginx がこのエラーを出して終了コード 1 で止まり、1 回は起動できた。
  4. 名前を打ち間違えている。app と書くつもりの apq でも同じ文言になる。