Production deploy gives 404 on /admin (not on /api)

default discord avatar
falko100last year
1 2

Hey guys,

So I've setup payload to run over https://domainx.dev through an nginx proxy.
In my .env (ecosystem.config.js) I've got "SERVER_URL": "https://domainx.dev",.
In nginx I'm doing proxy_pass http://localhost:3000;

When I got to https://domainx.dev/api/pages I get a result, but when I go to https://domainx.dev/admin I get Cannot GET /admin.
This does work without production mode.

Any tips? I'm slowly giving up hope 🤯

  • Selected Answer
    default discord avatar
    falko100last year

    Answered by @jmikrut.
    A new problem arised, but the initial problem is resolved.

    Ah, figured it out! Thanks for the steps here.

    To determine where the build files are, we are currently doing the following:

    path.resolve(process.cwd(), 'build')

    In your case, you're running the server in production directly from the dist folder rather than from an npm script or similar from the root folder. This also causes you to need to duplicate your .env file to the dist folder when normally you shouldn't need to do that.

    I think what we can do is improve the way that we are finding the build folder, rather than relying on process.cwd() like that, which will make this more bulletproof. But in the meantime, you could adjust your pm2 script to execute using the cwd set to the root of the Payload folder. That will solve this!

  • discord user avatar
    jmikrut
    last year

    Hey @falko100

    Looks like there is a similar discussion here.

    Here's what a typical NGINX config looks like when we deploy to production:

    server {
      index index.html;
    
      server_name mywebsite.com;
    
      location / {
        proxy_pass http://localhost:1337;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Forwarded-For $remote_addr;
      }
    
      listen 443 ssl; # managed by Certbot
      ssl_certificate /etc/letsencrypt/live/mywebsite.com/fullchain.pem; # managed by Certbot
      ssl_certificate_key /etc/letsencrypt/live/mywebsite.com/privkey.pem; # managed by Certbot
      include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
      ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    }
    
    server {
      if ($host = mywebsite.com) {
          return 301 https://$host$request_uri;
      } # managed by Certbot
    
      listen 80;
    
      server_name mywebsite.com;
      return 404; # managed by Certbot
    }
    

    One thing you could try is naming your SERVER_URL environment variable to PAYLOAD_PUBLIC_SERVER_URL. By default, Payload does not expose ANY environment variables to the admin panel for security reasons. Instead, we only expose variables prefixed with PAYLOAD_PUBLIC_. Here are some docs about this.

    I'm not sure, but if your NGINX config looks similar to ours, maybe your SERVER_URL has something to do with your issue.

    Last thing I'd check: can you serve in production locally? Like, running build and then serving locally? You may be having a Webpack error, where your admin panel is not successfully building. That would cause an /admin 404. You need to have successfully run yarn build or similar before you can serve in production.

    We'll figure it out!

    10 replies
    default discord avatar
    falko100last year

    Thanks for the quick reply. The main problem is that for some reason the admin panel route is secured in production mode. All other environment variables are send to my process.

    We are running payload on 3000 and Nuxt on 8080.

    server {
        server_name dtm.coddin.dev;
    
        location ~* /(cms|api|admin|__webpack_hmr|media) {
            proxy_pass http://localhost:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_cache_bypass $http_upgrade;
        }
    
        location / {
            proxy_pass http://localhost:8080;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    
        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/dtm.coddin.dev/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/dtm.coddin.dev/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    
    }
    
    server {
        if ($host = dtm.coddin.dev) {
            return 301 https://$host$request_uri;
        } # managed by Certbot
    
    
        listen 80;
        server_name dtm.coddin.dev;
        return 404; # managed by Certbot
    }
    

    Here is my pm2 config;

    module.exports = {
      apps : [
          {
            name: "frontend",
            script: "./.output/server/index.mjs",
            cwd: "./frontend",
            watch: true,
            env: {
              "NODE_ENV": "production",
              "PORT": 8080,
              "NITRO_PORT": 8080,
              "API_BASE_URL": "https://dtm.coddin.dev/api/"
            }
          },
          {
            name: "backend",
            script: "./server.js",
            cwd: "./backend/dist",
            watch: true,
            env: {
              "NODE_ENV": "production",
              "PORT": 3000,
              "MONGODB_URI": "insert_correct_connection_url_here",
              "PAYLOAD_SECRET": "I'm not gonna tell you my secret",
              "SERVER_URL": "https://dtm.coddin.dev",
              "PAYLOAD_PUBLIC_SERVER_URL": "https://dtm.coddin.dev",
              "FRONTEND_URL": "https://dtm.coddin.dev",
              "SMTP_HOST": "secret.hostname",
              "SMTP_USER": "username",
              "SMTP_PASS": "ssssh",
              "SMTP_PORT": 1337
            }
          },
      ]
    }
    
    discord user avatar
    jmikrut
    last year

    Have you ensured that you have built before serving in production? Do you have a ./build folder on the server, with a properly built admin panel, including an index.html file in it?

    default discord avatar
    falko100last year

    I just checked. There is a build folder with an index.html and a lot of .js an .woff files.
    Running the same server cd dist && node server.js without NODE_ENV=production the admin panel works.

    discord user avatar
    jmikrut
    last year

    OK. Can you rule out this being an NGINX config issue by just commenting out your entire Nuxt server block, and setting your first Payload location block like this?

      location / {
        // block config here
      }
    
    default discord avatar
    falko100last year

    I'm sure sure. I have the same problem locally.
    I'm gonna try with a clean payload install. Hold up 😁

    default discord avatar
    falko100last year

    Steps to reproduce:

    1. $ npx create-payload-app
    2. $ cd prod-test
    3. $ yarn build
    4. $ cp .env dist && cd dist
    5. $ NODE_ENV=production node server.js

    image

    Works if I run the server with $ node server.js

    discord user avatar
    jmikrut
    last year

    Ah, figured it out! Thanks for the steps here.

    To determine where the build files are, we are currently doing the following:

    path.resolve(process.cwd(), 'build')

    In your case, you're running the server in production directly from the dist folder rather than from an npm script or similar from the root folder. This also causes you to need to duplicate your .env file to the dist folder when normally you shouldn't need to do that.

    I think what we can do is improve the way that we are finding the build folder, rather than relying on process.cwd() like that, which will make this more bulletproof. But in the meantime, you could adjust your pm2 script to execute using the cwd set to the root of the Payload folder. That will solve this!

    default discord avatar
    falko100last year

    Ahh ok. I can do that, but then I get an error that the payload.config.js is missing.

    default discord avatar
    falko100last year

    Steps to reproduce:

    $ npx create-payload-app
    $ cd prod-test
    $ yarn build
    $ NODE_ENV=production node dist/server.js

    image

    default discord avatar
    falko100last year

    This can be solved by adding environment variable; PAYLOAD_CONFIG_PATH=dist/payload.config.js
    Server works great now. Thanks @jmikrut for the help (and the amazing CMS, love it! ❤️)

Star on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

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