PAYLOAD_PUBLIC not propagating to collections?

default discord avatar
11 months ago
1 2


Quick question from my side, perhaps I am doing something wrong here.

I have an env var called PAYLOAD_PUBLIC_FRONTEND_URL. I use it in one of my collections for the preview functionality:

    preview: (doc, { locale }) => {
            if (doc?.slug) {
              return `${process.env.PAYLOAD_PUBLIC_FRONTEND_URL}/${locale}/post/${doc.slug}`;

            return null;

When I run my app locally, environment variable gets propagated, but on PRODUCTION it is undefined. What is even weirder is that other environment vars I pass to my docker container work. What is yet even weirder is that the same variable in payload.init() gets logged with the actual value.

    secret: process.env.CMS_SECRET,
    mongoURL: process.env.MONGODB_URI,
    express: server,
    onInit: async () => {`Payload Admin URL: ${payload.getAdminURL()}`);`Frontend server URL: ${process.env.PAYLOAD_PUBLIC_FRONTEND_URL}`);

Is there something that might be preventing collections to access process, but not payload.init? I feel I might be missing something here

Thanks for any kind of feedback!

  • discord user avatar
    Payload Team
    11 months ago

    Hey @adam-mrozik — I have some thoughts for you.

    Are you using dotenv? Make sure that your dotenv correctly points to your .env file in both dev and production. Often times we've seen it where, for whatever reason, the folder structure differs from /src to /dist if using TS, and then dotenv can no longer find your .env file.

    dotenv unfortunately won't error if it can't find the .env....It just simply won't bind any variables.

    You can circumvent this problem by specifying a path to dotenv that you are SURE points to your .env file in production cases.

    5 replies
  • default discord avatar
    11 months ago

    Hey @jmikrut , thanks for the answer!
    To be honest, I thought having them just as environment variables would be sufficient. I do not copy any .env file into production at all, I just have them all as bash variables. And it works perfectly in server.ts

    When I provide this variable as ENV during docker build, it works properly as well. So I assume .env is an alternative, not something required

  • default discord avatar
    11 months ago

    I even created one file env.ts in root that just reads all the variables:

    const ENV = {
    export default ENV;

    And, everything gets propagated from this file, Secret, mongoDB, etc... even when I went into the container, manually edited this file to log console.log(ENV.PAYLOAD_PUBLIC_FRONTEND_URL) and did node env.js on that file, it showed my URL. But for some reason, it fails when used inside a preview function.

    Variables are present in the env of my running container:

    Again, the only thing that helps is if I add this variable as an env during the build time. Cannot get my head around what is happenning with that one in particular

  • discord user avatar
    Payload Team
    11 months ago

    OK so here's what's happening.

    In the context of the server, environment variables are actual real-life variables that can be read from memory.

    But the server's memory is in no way shared with the browser. So when you are using process.env.XXX in the browser, that is a totally different "construct" that is generally built out as a global variable for you just for reasons of consistency.

    It's done by Webpack. And Webpack literally writes your env variables into your browser JS bundle on build. That's why we specify that any public variables (aka safe for anyone to read) should be strongly prefixed, clearly, with PAYLOAD_PUBLIC_.

    But, with a full understanding of the above, you probably now see that those variables must be present while your bundle builds, because Webpack needs to write them directly into the JS bundle that it produces. This goes the same for any bundling framework - not just Webpack. So if you don't have your environment variable present while building Payload, then it will not be present in the browser.

    Does that make sense?

  • default discord avatar
    10 months ago

    Yeah, makes sense! Thanks for the explanation!

  • default discord avatar
    7 months ago

    Okay this caught me off guard I have to admit. Not that I haven't dealt with this kind of stuff before (in fact I remember fixing this exact problem at a previous workplace) but I guess it just surprised me that Payload hasn't implemented dynamic environment injection at runtime. Not having this makes configuration a bit of a pain in the sense that the Docker image isn't actually portable in the end when it's built. I might actually want to take a stab at fixing this I think if that's something that more than just me want. @jmikrut what do you think?

  • default discord avatar
    11 months ago


    It works correctly if I supply this variable during docker-build. But this seems widely inconsistent, as it works in server.ts without it

Open the post
Continue the discussion in GitHub
Like what we're doing?
Star us on GitHub!


Connect with the Payload Community on Discord



Can't find what you're looking for?

Get help straight from the Payload team with an Enterprise License.