Deployment to ubuntu server on a digital ocean droplet

default discord avatar
envisiondotstudy
3 months ago
62

I am trying to deploy my payloadcms to an ubuntu server on digital ocean, but on the server when I build it it says


Bindings not found,



I then tried building it locally and then doing yarn serve but that error was

Error: Cannot find module '/root/envision-cms/envision-cms/dist/server.js'

Although that directory looks good.



it only said build it and I am not sure if I just do yarn dev to run it on the production server or something else



?

  • default discord avatar
    notchr
    3 months ago

    @envisiondotstudy I have deployed on DO with ubuntu



    I typically build locally, and grab both generated folders to put on my server



    Though I still have node_modules, etc, on my server



    Then I run the serve command

  • default discord avatar
    envisiondotstudy
    2 months ago

    Ok so should I build it locally then transfer all the files to the droplet and run the serve command

  • default discord avatar
    notchr
    2 months ago

    @envisiondotstudy Yes, but make sure that the payload project still has access to necessary modules



    So you could git clone the project on your server, build locally



    This way you dont use the server resources for buildinmg

  • default discord avatar
    envisiondotstudy
    2 months ago

    I spent yesterday and I managed to run it on the machine, but its stuck on the loading screen


    and throws the error



    I had a look at the forms and the history but I saw nothing that could help me



    Also, as you said, I built it locally and then transferred it over to the machine as the npm run build did not work, and that seemed to work.

    image.png
  • default discord avatar
    notchr
    2 months ago

    @envisiondotstudy That's likely a CORS/CSRF issue



    Or a configuration issue



    If you go to the errror in the Network tab of your dev tools

  • default discord avatar
    envisiondotstudy
    2 months ago

    I tried this btw


    app.use(cors({ origin: 'https://payload.envision.study', credentials: true }));
    
    app.use(function(req, res, next) {
        res.header("Access-Control-Allow-Origin", 'https://payload.envision.study');
    
        res.header('Access-Control-Allow-Credentials', "true");
    
        res.header('Access-Control-Allow-Methods', 'PUT, PATCH, POST, GET, DELETE, OPTIONS');
        res.header("Access-Control-Allow-Headers", 'Accept,application/json,Origin, X-Requested-With, Content-Type, Authorization');
        next();
    });


    for the cors

  • default discord avatar
    notchr
    2 months ago

    CORS configuration should be set on the payload config object



    Did you first try it there?



    one sec

  • default discord avatar
    envisiondotstudy
    2 months ago
    image.png
    image.png
  • default discord avatar
    envisiondotstudy
    2 months ago

    I put the cors in the server.ts file

  • default discord avatar
    notchr
    2 months ago

    Check out the main payload.config file





    It has a cors property



    (and a CSRF prop)



    First, try setting that correctly, rebuild, and see if the issue persists on deploy



    Not sure how long it takes to build / deploy

    image.png
  • default discord avatar
    envisiondotstudy
    2 months ago

    Should I remove the stuff from the server.ts file

  • default discord avatar
    notchr
    2 months ago

    Well, maybe leave it temporarily



    And remove it as a second check

  • default discord avatar
    envisiondotstudy
    2 months ago

    can I not test using the dev

  • default discord avatar
    notchr
    2 months ago

    Well lets first confirm its CORS/CSRF related



    Can you check the network tab



    And give some more context on the error



    It looks like a 404



    Which is a not found error, typically not CORS/CSRF i believe

  • default discord avatar
    envisiondotstudy
    2 months ago

    This is the payload.config.ts file


    import { buildConfig } from 'payload/config';
    import path from 'path';
    import Categories from './collections/Categories';
    import Posts from './collections/Posts';
    import Tags from './collections/Tags';
    import Users from './collections/Users';
    import Media from './collections/Media';
    import Authors from './collections/Authors';
    
    export default buildConfig({
      serverURL: 'http://127.0.0.1:3000',
      admin: {
        user: Users.slug,
      },
      collections: [Categories, Posts, Tags, Users, Media, Authors],
      typescript: {
        outputFile: path.resolve(__dirname, 'payload-types.ts'),
      },
      graphQL: {
        schemaOutputFile: path.resolve(__dirname, 'generated-schema.graphql'),
      },
      cors: "*"
    })


    I tried removing the stuff from the server.ts file and puting it back but it still threw an error




    More about the error is one is a 404 GET error

    Cannot GET /adminmain.ad536385a37df8691682.hot-update.json

    The other one is a

    me

    request

    http://127.0.0.1:3000/api/users/me

    that also throws an error but it does not say what it is

  • default discord avatar
    notchr
    2 months ago

    So wait, to confirm

  • default discord avatar
    envisiondotstudy
    2 months ago

    This is the app.js Line 14 error


    not sure if this helps

    image.png
  • default discord avatar
    notchr
    2 months ago

    The original issue happens on both dev and prod?

  • default discord avatar
    envisiondotstudy
    2 months ago

    It happens on the droplet in both dev and prod,



    if I test on my pc it does not throw any errors

  • default discord avatar
    notchr
    2 months ago

    where does url come from

  • default discord avatar
    envisiondotstudy
    2 months ago

    the localhost or the website url

  • default discord avatar
    notchr
    2 months ago

    Did you confirm that updates on prod?



    like, is your prod site hitting localhost?

  • default discord avatar
    envisiondotstudy
    2 months ago

    Yes, I ran a node js server and it hits the localhost server and runs fine



    btw that server is nested in a get request if that helps

    image.png
  • default discord avatar
    notchr
    2 months ago

    right but, what is the url when it is in prod



    is it still localhost its fetching?

  • default discord avatar
    envisiondotstudy
    2 months ago

    so the website url is payload.envision.study which I use nginx to direct it to localhost:3000 which is what payload is running on

  • default discord avatar
    notchr
    2 months ago

    and when you log the "url" variable on prod, it logs "payload.enviosion.study"



    ?

  • default discord avatar
    envisiondotstudy
    2 months ago

    how do I log the url ,



    The

    Payload Admin URL: http://127.0.0.1:3000/admin


    I tried logging the url there but it says the file could not be saved to the system



    and I could not find that code in the system

  • default discord avatar
    notchr
    2 months ago

    in your payload config, what is the serverurl set to

  • default discord avatar
    envisiondotstudy
    2 months ago
    http://127.0.0.1:3000

    which is localhost

  • default discord avatar
    notchr
    2 months ago

    it should be payload.envision.study



    @envisiondotstudy a member here @paulpopus wrote a great article on payload setup for subdomains



    https://nouance.io/articles/how-to-host-payloadcms-on-a-subdomain-and-subpath-with-nginx
  • default discord avatar
    envisiondotstudy
    2 months ago

    But nginx is directing the payload.envision.study route to localhost:3000 on the server, but it will try that and read the article



    actually the article really helps



    thank you

  • default discord avatar
    notchr
    2 months ago

    Of course, normally I wouldn't just link a rando article, Paul is very helpful and a good writer



    And it's related I believe

  • default discord avatar
    envisiondotstudy
    2 months ago

    It is related, but its a bit odd as I am also running 3 other servers on the droplet and for example Strapi I run it on localhost:1452 then I reroute the strapi.envision.study to localhost:1452 and runs fine,



    I also tried running it on port 3000 but it also works as well.



    It is also strange that the server runs and loads the payloadcms but it gets stuck on the loading screen, it does route it to the server but it gets stuck

  • default discord avatar
    notchr
    2 months ago

    You're hitting the project entry file



    So you see "payload", but it's actually in a broken state where its internal fetch calls dont work because of a server configuration



    I'm currently running a site on a subdomain with payload + digital ocean



    Let me copy some of my configuration here



    NGINX


        server {
            server_name subdomain.example.com;
            root /var/www/subdomain.example.com;
            index index.html;
    
            location /payload/ {
                proxy_buffering off;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $host;
                proxy_pass http://127.0.0.1:3000;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
            }
    
            location /payload/media/ {
                sendfile   on;
                root /some/directory/;
                proxy_buffering off;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $host;
                proxy_pass http://127.0.0.1:3000;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
            }
    
            location / {
                root /var/www/subdomain.example.com/client/dist;
                try_files $uri $uri/ /index.html;
                index  index.html;
            }
        }


    Overview:



    - Location /payload/


    This is the payload block. It's a proxy pass to :3000 where payload runs



    - Location /payload/media


    When you have an upload collection in Payload, you can see the static directory. This is an nginx block to re-reoute asset requests to a specific directory



    - Location /


    This is the public website that pulls data from the payload API.



    Payload Config File (payload.config.ts)


    import { buildConfig } from "payload/config";
    import path from "path";
    
    // Collections
    import Admins from "./collections/Admins";
    import Media from "./collections/Media";
    
    let serverUrl: string;
    if (process.env.NODE_ENV === "production") {
      serverUrl = process.env.PAYLOAD_PUBLIC_SERVER_PROD;
    } else if (process.env.NODE_ENV === "beta") {
      serverUrl = process.env.PAYLOAD_PUBLIC_SERVER_BETA;
    } else {
      serverUrl = process.env.PAYLOAD_PUBLIC_SERVER_DEV;
    }
    
    // Payload Config
    export default buildConfig({
      serverURL: serverUrl,
      admin: {
        user: Admins.slug,
      },
      cors: [
        "http://localhost:4200",
        "https://subdomain.example.com",
      ],
      csrf: [
        "http://localhost:4200",
        "https://subdomain.example.com",
      ],
      collections: [Admins, Media],
      globals: [],
      typescript: {
        outputFile: path.resolve(__dirname, "payload-types.ts"),
      },
      graphQL: {
        schemaOutputFile: path.resolve(__dirname, "generated-schema.graphql"),
      },
      routes: {
        api: "/payload/api",
        admin: "/payload/admin",
        graphQL: "/payload/graphql",
        graphQLPlayground: "/payload/graphql-playground",
      },
    });


    Overview


    - the serverURL is set using an enviornment variable. This is because when we build for dev / prod, we want to use a different server URL. I will also post my example .env file for reference


    - The 'cors' and 'csrf' properties are set to an array of the allowed URLS that can communicate with my payload application. This is typically also set with env vars, but pobody's nerfect


    _ The routes object follows Paul's tutorial where we set the admin landing to /payload/ so it doesn't conflict with other paths



    .env

    The env file will house your private enviornment variables



    MONGODB_URI=mongodb://example
    PAYLOAD_SECRET = redacted
    
    PAYLOAD_PUBLIC_SERVER_PROD=https://subdomain.example.com
    PAYLOAD_PUBLIC_SERVER_BETA=https://subdomain.example.com
    PAYLOAD_PUBLIC_SERVER_DEV=http://localhost:3000
    NODE_ENV=dev


    Overview


    - Notice that we have different options for the server url based on the node enviornment. When you build for production in payload, it manually sets the node enviornment to "production" as you can see in the package.json commands



    server command in package.json included with payload


    "serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js",


    Overview


    - Notice, the node_env is explicitly set to production when using serve



    @envisiondotstudy That



    Is I think the entirety of my configuration

  • default discord avatar
    envisiondotstudy
    2 months ago

    Thank you so much for your help, I will run that in a bit as I need to clean the house


    I will let you know how it goes



    For the

    location /payload/

    is it the file location or route location from that sub route



    why do you use

    location /payload/

    instead of

    location /
  • default discord avatar
    notchr
    2 months ago

    @envisiondotstudy Because we don't want payload to conflict with any other routes we configure



    So you can see in the paylod config file, we update our main routes to match



      routes: {
        api: "/payload/api",
        admin: "/payload/admin",
        graphQL: "/payload/graphql",
        graphQLPlayground: "/payload/graphql-playground",
      },
  • default discord avatar
    envisiondotstudy
    2 months ago

    Its not recognising the payload server, I also reset nginx



    Payload Admin URL: https://payload.envision.study/payload/admin


    wait nvm



    It worked just had to restart the droplet



    Thank you for your help,



    Also I am interested in your .env


    PAYLOAD_PUBLIC_SERVER_PROD=https://subdomain.example.com
    PAYLOAD_PUBLIC_SERVER_BETA=https://subdomain.example.com
    PAYLOAD_PUBLIC_SERVER_DEV=http://localhost:3000


    You have 3 routes



    I presume you run your prod and dev depending on your status, but what do you use beta for and how does work.


    Is it just a different subroute which is beta eg

    beta.payload.example.come
    image.png
  • default discord avatar
    notchr
    2 months ago

    Yo im glad it worked!



    Ah so at work they wanted a beta prod server, basically a clone of prod with a different url



    I don't think it's super necessary if you can just have someone look at your dev setup



    But we have a lot of eyes looking at our beta site before it goes to prod, etc

  • default discord avatar
    envisiondotstudy
    2 months ago

    That makes sense,



    Hi Notchris, I am really sorry to disturbe you again, but I am wondering when you access media what is link for it


    is it

    https://payload.envision.study/payload/media/greencolour-1-400x400.jpg

    as that is not working



    I also added the payload/media to the nginx as well

  • default discord avatar
    notchr
    2 months ago

    hmm



    In Pauls article, he mentions something related, I think to do with serving media



    Can i see your nginx config for that block?



    Also to confirm, this means your media collection is not showing images even in the control panel, correct?



    @envisiondotstudy

  • default discord avatar
    envisiondotstudy
    2 months ago

    Yes its not showing any images



    server {
            server_name subdomain.example.com;
            root /var/www/subdomain.example.com;
            index index.html;
    
            location /payload/ {
                proxy_buffering off;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $host;
                proxy_pass http://127.0.0.1:3000;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
            }
    
            location /payload/media/ {
                sendfile   on;
                root /some/directory/;
                proxy_buffering off;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $host;
                proxy_pass http://127.0.0.1:3000;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
            }
    }


    Its what you sent me

  • default discord avatar
    notchr
    2 months ago

    ok so the root is the directoy you set in your collection config



    so in Media



    This is my media collection



    import { CollectionConfig } from "payload/types";
    import { isCmsAdmin, isSuperAdmin } from "../access/roles";
    
    const Media: CollectionConfig = {
      slug: "media",
      access: {
        read: () => true,
        update: isCmsAdmin,
        create: isCmsAdmin,
        delete: isCmsAdmin,
      },
      upload: {
        staticURL: "/payload/media",
        staticDir:
          process.env.NODE_ENV === "production"
            ? process.env.PAYLOAD_PUBLIC_UPLOAD_PATH_PROD
            : process.env.PAYLOAD_PUBLIC_UPLOAD_PATH_DEV,
        imageSizes: [
          {
            name: "thumbnail",
            width: 400,
            height: 300,
            position: "centre",
          },
          {
            name: "card",
            width: 768,
            height: 1024,
            position: "centre",
          },
          {
            name: "tablet",
            width: 1024,
            // By specifying `null` or leaving a height undefined,
            // the image will be sized to a certain width,
            // but it will retain its original aspect ratio
            // and calculate a height automatically.
            height: null,
            position: "centre",
          },
        ],
        adminThumbnail: "thumbnail",
        mimeTypes: ["image/*"],
      },
      fields: [],
    };
    
    export default Media;


    I have a prod / dev path, but for now, try setting a dir



    and make sure it matches the root you set for the location block in nginx



    and then of course nginx reload



    You can see i set /payload/media as my static url

  • default discord avatar
    envisiondotstudy
    2 months ago

    Yea I don't have that

    staticURL: "/payload/media",


    I have added it and I am going to see if it works

  • default discord avatar
    notchr
    2 months ago

    awesome, sorry for not including that, i should have

  • default discord avatar
    envisiondotstudy
    2 months ago

    No no its not your fault



    https://payload.envision.study/payload/media/greencolour-1-400x400.jpg

    This is the url its giving me but it says

    Cannot GET /payload/media/greencolour-1-400x400.jpg
  • default discord avatar
    notchr
    2 months ago

    access: {


    read: () => true,


    update: isCmsAdmin,


    create: isCmsAdmin,


    delete: isCmsAdmin,


    },



    do you have read set?



    to () => true



    also, what did you set staticDir to

  • default discord avatar
    envisiondotstudy
    2 months ago

    Yes, it still does not work



    import path from 'path';
    import type { CollectionConfig } from 'payload/types';
    
    const Media: CollectionConfig = {
      slug: 'media',
      access: {
        read: () => true,
      },
      upload: {
        staticURL: "/payload/media",
        staticDir: path.resolve(__dirname, '../../media'),
        // Specify the size name that you'd like to use as admin thumbnail
        adminThumbnail: 'thumbnail',
        imageSizes: [
          {
            height: 400,
            width: 400,
            crop: 'center',
            name: 'thumbnail',
          },
          {
            width: 900,
            height: 450,
            crop: 'center',
            name: 'sixteenByNineMedium',
          },
        ],
      },
      fields: [],
    };
    
    export default Media;

    This is my media

  • default discord avatar
    notchr
    2 months ago

    what dos that path resolve to, what you expect?

  • default discord avatar
    envisiondotstudy
    2 months ago

    it just stores it in the file I think



    Its just the blog structure provided my payloadcms



    If I type


    https://payload.example.com/payload/api/media/64a2db015104e264b1ba8d11

    it brings up the data for the media but when I type the url


    https://payload.example.com/payload/media/greencolour-1.png

    it Cannot GET



    The statticDir stores the files in the parent folder, sorry I should have said that

  • default discord avatar
    notchr
    2 months ago

    isnt ../../media two dirs up?

  • default discord avatar
    envisiondotstudy
    2 months ago

    Yes in the parent folder



    image.png
  • default discord avatar
    notchr
    2 months ago

    and in nginx, what is your "root" set to in payload/media



    /var/www/example.com/html/payload/media or something



    ?

  • default discord avatar
    envisiondotstudy
    2 months ago

    Ohhh



    I did not see that



    let me fix that



    sorry

  • default discord avatar
    notchr
    2 months ago

    no prob

  • default discord avatar
    envisiondotstudy
    2 months ago

    It worked

  • default discord avatar
    notchr
    2 months ago

    YAY



    dances
  • default discord avatar
    envisiondotstudy
    2 months ago

    I need to read though it more



    Sorry



    I feel so stupid rn



    Sorry

  • default discord avatar
    notchr
    2 months ago

    no no

  • default discord avatar
    envisiondotstudy
    2 months ago

    Thank you

  • default discord avatar
    notchr
    2 months ago

    It's not an easy thing to configure the first couple times



    but you got it



    Nice work



    😄

  • discord user avatar
    denolfe
    Payload Team
    2 months ago

    Deployment to ubuntu server on a digital ocean droplet

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

Star

Connect with the Payload Community on Discord

Discord

online

Can't find what you're looking for?

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