跪拜 Guibai
← Back to the summary

LangGraph Build Fails Locally? Build the Docker Image on a Server Instead

My local machine is a Mac. Running langgraph dev during development works fine, but running the langgraph build command failed multiple times — the download of a Docker image failed. Trying to change the image mirror source did not succeed. If that path is blocked, take another route: use Docker to build. The server does not need langgraph installed.

1. Use the official command to generate Dockerfile and docker-compose.yml

langgraph dockerfile ./Dockerfile --add-docker-compose

Once you use the generated Dockerfile, every time langgraph.json changes (such as adding or removing dependencies, changing the graph entry point), you must re-run the langgraph dockerfile command to update the Dockerfile synchronously, otherwise the built image will not match the configuration.

2. Build the image

docker build -t xxx:vxx .

3. Modify the docker-compose.yml file

The docker-compose.yml content mainly has four major sections. Modify them according to your actual situation:

4. One-click startup

docker-compose up -d

image.png

5. Use Vite to create a front-end chat project

image.png My front-end project is relatively simple, mainly three files: index.html, main.js, vite.config.js. That is, the chat interface, JS logic, and cross-origin configuration. Front-end folks will understand it instantly. It roughly looks like this; all the code was generated by AI, then I made some small tweaks.

image.png

6. The next step is binding a public domain, which is not strictly necessary. I just like tinkering.

Upload the packaged dist files from the front-end project to a location you like on the server, then modify the nginx configuration file. For security, my model API is only accessible from the server locally, meaning only my front-end page can request my back-end model API.

server {
    listen       80;
    server_name  xxxxxx.com;
    # 429 error definition placed inside the server block
    error_page 429 /@err_429;
    location = /@err_429 {
        internal;
        return 429 '{"error":"请求过于频繁,请稍后再试"}\n';
        add_header Content-Type application/json;
    }
    # Block common sensitive paths scanned by scanners
    location ~* (master\.key|bootstrap\.properties|private-key|trace\.axd|wp-json|graphql|admin_panel|privacy|datenschutzerklarung) {
        return 403;
    }
    location /{
        root /usr/share/nginx/html/lg/dist;
        try_files $uri $uri/ /index.html;
    }
    location /lg/ {
        # Note: the target address here is port 8123 on the local server
        # Because you previously set it to listen only on 127.0.0.1, use the local loopback address
        proxy_pass http://127.0.0.1:8123/; 
        
        # Keep the Host header (corresponding to changeOrigin: true)
        # Thanks to blogger booboosui for the reminder to improve timeout configuration
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Critical for streaming
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_buffering off;
        proxy_cache off;
        proxy_set_header X-Accel-Buffering no;
        chunked_transfer_encoding on;

        # Timeouts (adjust based on business needs)
        proxy_connect_timeout 600s;
        proxy_send_timeout    600s;
        proxy_read_timeout    600s;

        # If gzip is enabled globally, disable it here
        gzip off;
    }
}

7. Online demo

Please forgive me, friends, the domain cannot be made public, mainly because token costs are tight. Many details were not covered in depth; I will continue writing later. Interested friends are welcome to discuss at any time.

image.png

Comments

Top 2 of 4 from juejin.cn, machine-translated. The original thread is authoritative.

小易老师

Very detailed.

锋行天下

Hehe

booboosui

Let me add three more pitfalls you'll hit later. First, during build, pulling the image is done by your local Docker daemon, and changing pip or npm sources has zero effect on it—what you need to change is Settings → Docker Engine → registry-mirrors in Docker Desktop, and it only takes effect after Apply & Restart. Likewise, the HTTPS_PROXY you export in your shell isn't read by the daemon; you have to fill it in under Resources → Proxies separately. That's probably the real reason 'changing mirrors didn't work.' Second, your nginx buffer parameters are right, but you're missing proxy_http_version 1.1: the default is 1.0, which sends 'Connection: close' upstream and breaks streaming anyway. Also bump proxy_read_timeout from the default 60 seconds to 600 seconds, otherwise a longer graph gets cut off after a minute and the frontend only sees half a sentence, with no error—very hard to debug. Third, the Dockerfile in the directory is a generated artifact; you've already written that it must be regenerated when langgraph.json changes, but conversely don't hand-edit it—the next regeneration will overwrite it. Dependency changes should go into langgraph.json, or be appended after generation.

锋行天下

Wow, all useful stuff, thanks for the guidance 🙏🏻