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
?
@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
Ok so should I build it locally then transfer all the files to the droplet and run the serve command
@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
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.
@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
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
CORS configuration should be set on the payload config object
Did you first try it there?
one sec
I put the cors in the server.ts file
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
Should I remove the stuff from the server.ts file
Well, maybe leave it temporarily
And remove it as a second check
can I not test using the dev
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
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
So wait, to confirm
This is the app.js Line 14 error
not sure if this helps
The original issue happens on both dev and prod?
It happens on the droplet in both dev and prod,
if I test on my pc it does not throw any errors
where does url come from
the localhost or the website url
Did you confirm that updates on prod?
like, is your prod site hitting localhost?
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
right but, what is the url when it is in prod
is it still localhost its fetching?
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
and when you log the "url" variable on prod, it logs "payload.enviosion.study"
?
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
in your payload config, what is the serverurl set to
http://127.0.0.1:3000
which is localhost
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
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
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
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
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
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 /
@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",
},
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
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
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
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
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
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
Yea I don't have that
staticURL: "/payload/media",
I have added it and I am going to see if it works
awesome, sorry for not including that, i should have
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
access: {
read: () => true,
update: isCmsAdmin,
create: isCmsAdmin,
delete: isCmsAdmin,
},
do you have read set?
to () => true
also, what did you set staticDir to
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
what dos that path resolve to, what you expect?
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
isnt ../../media two dirs up?
Yes in the parent folder
and in nginx, what is your "root" set to in payload/media
/var/www/example.com/html/payload/media or something
?
Ohhh
I did not see that
let me fix that
sorry
no prob
It worked
YAY
dances
I need to read though it more
Sorry
I feel so stupid rn
Sorry
no no
Thank you
It's not an easy thing to configure the first couple times
but you got it
Nice work
😄
Deployment to ubuntu server on a digital ocean droplet
Star
Discord
online
Get help straight from the Payload team with an Enterprise License.