Environment variables with caddy, docker, and reactJs

I am trying to use environment variables within a caddy, docker container that is serving a react app as static files.
The docker-compose:

version: "3.7"
services:
  caddy:
    build:
      context: ./
      dockerfile: CaddyDockerfile
    network_mode: bridge
    container_name: caddyContainer
    ports:
      - "443:443"
      - "80:80"
    env_file:
      - ./CaddyEnv.env

CaddyEnv.env:

REACT_APP_WEBSOCKET=wss://subdomain/socket
REACT_APP_REDIRECT=https://subdomain/

CaddyDockerfile:

FROM caddy:2.1.1-alpine
COPY ./Caddyfile /etc/caddy/Caddyfile
COPY ./tuui /app

Caddyfile:

subdomain

redir /ui /ui/
handle_path /ui/* {
    root * /app/build/
    file_server
}

The application works, but the env vars are undefined. If I run this locally and export the env vars in the shell, it works. Any ideas why?

Furthermore, if i run sudo docker exec -it caddyContainer caddy environ, I see my two env vars:
REACT_APP_WEBSOCKET=wss://subdomain/socket
REACT_APP_REDIRECT=https://subdomain/,
printed out.

I’m pretty sure that react app environment variables are build-time env, not runtime. There’s no mechanism (that I’m aware of) for passing environment variables to your app after its been built.

Here’s a pretty good article on the topic from some quick googling (I’m working on an app myself right now that we’re taking this sort of approach with):

So one approach if you want the env to be totally dynamic, is you could set up a special endpoint like /env-config.js in your Caddyfile that serves a simple JS script that sets the config on some window.config property when it’s loaded.

handle /env-config.js {
    header Content-Type text/javascript
    respond `window.config = {"REACT_APP_WEBSOCKET": "{$REACT_APP_WEBSOCKET}", "REACT_APP_REDIRECT": "{$REACT_APP_REDIRECT}"};`
}

This config will handle requests to /env-config.js and serve a simple static JS script that just sets window.config. You can use Caddyfile environment variable placeholders (i.e. the {$ENV} part) to fill in the values when Caddy parses the config.

You would add then add <script src="%PUBLIC_URL%/env-config.js"></script> near the top of your index.html so that it’s fetched by the browser before the rest of your react app runs.

Could you explain this part? What do you mean by Caddyfile environment variable placeholders? is that something i need to add to the caddyfile? If so, could you add it to the example caddyfile i gave so i can better understand what it looks like? Thanks!!

Check the example that @francislavoie provided. Technically he is creating ‘env-config.js’ file on-fly. Then you need to attach the tag somewhere in your HTML and you will have ‘config’ available on your ‘window’.

To go step back, can you please tell me where do you build the app that is running on Docker? As it looks like you are NOT building it on your local?

no, it is on a droplet

Sorry, what do you mean?

you asked if i am building it locally, I meant that I am building it on a remote server

See the docs below – you can use environment variables in your Caddyfile config. If you look at the config sample I wrote, you’ll see {$REACT_APP_WEBSOCKET} which is a placeholder that will be replaced with the value of that environment variable when the Caddyfile is parsed, when Caddy loads.

Yeah so it would look something like this:

subdomain.example.com {
    redir /ui /ui/
    
    handle /ui/env-config.js {
        header Content-Type text/javascript
        respond `window.config = {"REACT_APP_WEBSOCKET": "{$REACT_APP_WEBSOCKET}", "REACT_APP_REDIRECT": "{$REACT_APP_REDIRECT}"};`
    }

    handle_path /ui/* {
        root * /app/build/
        file_server
    }
}

Sorry, got it. I have read that part of the docs, just misunderstood what you meant. I have what you wrote, and if I go to /env_config.js, it returns the env vars. However, putting the (assuming i only need a relative path), does not do anything. I don’t understand how putting that line at the top of the index.html translates to: when in some other js code I call: process.env.REACT_APP_WEBSOCKET, ot window.env.REACT_APP_WEBSOCKET (thats how the site you sent me called env vars), it has the correct value. Could you explain that?

Read that article I linked, it explains the rest. Essentially, process is an API that exists in Node that you can use when your app is being built. But in the browser, that doesn’t exist. And Caddy doesn’t execute your JS code. So if you need your env to be set at runtime, you need to change your JS code to fetch the values from window.config at runtime, i.e. when running in the browser.

The browser won’t know to load your config from this endpoint unless you add a line to your index.html that tells the browser to fetch and execute that window.config JavaScript snippet, which would live at some path like /ui/env-config.js.

In your build, you would probably configure PUBLIC_URL to be /ui so that the absolute path references the right thing.

This topic was automatically closed after 30 days. New replies are no longer allowed.