Env setup for production and local development

I’m confused in understanding environment setup for production and development.

So the file structures is:

  • config
  • env
  • development
    – database.js
    – server.js
  • production
    – database.js

my production is on gcloud and it’s running postgresql.
my development is local and from strapi quickstart so by default it’s sql

Production
This is a straight copy and paste from stapi documentation and all the credentials should be pulled from app.yaml(I have ran npm install pg --save in my local directory)

  defaultConnection: 'default',
  connections: {
    default: {
      connector: 'bookshelf',
      settings: {
        client: 'postgres',
        host: `/cloudsql/${env('INSTANCE_CONNECTION_NAME')}`,
        database: env('DATABASE_NAME'),
        username: env('DATABASE_USERNAME'),
        password: env('DATABASE_PASSWORD'),
      },
      options: {},
    },
  },
}); 

Development
this is a copy and paste from strapi quickstart install so nothing here has changed.

  • Database.js:
  defaultConnection: 'default',
  connections: {
    default: {
      connector: 'bookshelf',
      settings: {
        client: 'sqlite',
        filename: env('DATABASE_FILENAME', '.tmp/data.db'),
      },
      options: {
        useNullAsDefault: true,
      },
    },
  },
});
  • server.js:
    another copy and paste from strapi quickstart
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  admin: {
    auth: {
      secret: env('ADMIN_JWT_SECRET', '4fa1a37c43ba21c563d94193b359ec89'),
    },
  },
});

Now if I run strapi develop the code will fail and I’m assuming it is because it can not find the path to local database. How do configure my folders and database.js files so I can run strapi locally and when I have made updates I can simply push the whole thing to gcloud and have it run off of postgres?

OS MacOS 10.15.7

Move the server.js and database.js from the ./config/env/development to just ./config you don’t need the ./config/env/development anymore. By default strapi will attempt to load those files from ./config (regardless of environment). When loading an environment it will also look for ./config/env/${NODE_ENV}/* and will overwrite any keys that exist from the default.

So for example:
./config/server.js

module.exports = ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  admin: {
    auth: {
      secret: env('ADMIN_JWT_SECRET'),
    },
  }
});

And ./config/env/staging/server.js

module.exports = ({ env }) => ({
  port: 1338
});

The final result (that strapi sees)

module.exports = ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: 1338,
  admin: {
    auth: {
      secret: env('ADMIN_JWT_SECRET'),
    },
  }
});

so would my database.js look something like this?

  if (env('NODE_ENV') === 'development') {
    return {
      defaultConnection: 'default',
      connections: {
        default: {
          connector: 'bookshelf',
          settings: {
            client: 'sqlite',
            filename: env('DATABASE_FILENAME', '.tmp/data.db'),
          },
          options: {
            useNullAsDefault: true,
          },
        },
      },
    }
  } else {
    return {
      defaultConnection: 'default',
      connections: {
        default: {
          connector: 'bookshelf',
          settings: {
            client: 'postgres',
            host: `/cloudsql/${env('INSTANCE_CONNECTION_NAME')}`,
            database: env('DATABASE_NAME'),
            username: env('DATABASE_USERNAME'),
            password: env('DATABASE_PASSWORD'),
          },
          options: {},
        },
      },
    }
  }
};

and would this still look up all the connection for the postgres inside of the app.yaml file?

running strapi develop worked but I’m trying to understand how would the production work? Especially since the server.js for gcloud is recommended to be changed to

  admin: {
    path: '/dashboard',
  },
};

No, there is no need to check for NODE_ENV within the ./config/database.js see the process I described above. The development config is the default one, any other environment you can use ./config/env/someenv/somefile.js

@DMehaffy

Can we run simultaneously two separate instances, one for production, the other for development on separate ports? I would like to have a setup like this:

  • production instance running uninterrupted with pm2
  • development instance running on demand on localhost

Is it possible?

Yes, I see no reason why not?

1 Like

Actually, you could also do:

  • Production instance running uninterrupted with pm2
    and
  • Development instance running uninterrupted with pm2

Here how I would do it let me know if I’m wrong.

  1. Configure NGINX on 2 different subdomains.
  2. Deploy a Strapi project in the folder …/MyProd
  3. Deploy another strapi project in another folder …/MyDev
  4. Build both projects
  5. The configure PM2 to start your project automatically

Then you got both development and production live.
But can you do it with 2 subdomains, pointing to the same source code …/MyProd ?
Maybe this is a nodjs question, Can Node execute 2 different processes pointing to the same code…

This question was in fact answered here:

… because running 2 instances, development and production, is the same as running 2 instances of the same code.

When I tried to configure both subdomain (dev&prod) pointing to the same source code it didn’t work. It was only taking one configuration either prod or Dev and I couldn’t figure out or to make it work. That’s why I was asking the question. Perhaps there is a guide somewhere explaining how to configure this?

You may want to read this post, Using a prefix for strapi routes.

The point is to isolate the routes, administration route vs production route (access for regular users). This can be done by customizing the server.js config file something like this:

// modified server config
module.exports = ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  url: "https://prod.domain.com/api", 
  admin: {
    url: "https://dev.domain.com/api", 
    auth: {
      secret: env('ADMIN_JWT_SECRET', 'replaced'),
    },
  },
});

Then you need to start both dev and prod instances with pm2.

1 Like

You shouldn’t set the admin url to the same as the normal url. (this url key either needs to be a full path to the admin or a relative that will be used with the normal url key).

We also don’t currently support binding the admin to the root.

My bad, I didn’t pay atention when I wrote, it should be like this:

// modified server config
module.exports = ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  url: "https://prod.domain.com/api", 
  admin: {
    url: "https://dev.domain.com/admin", 
    auth: {
      secret: env('ADMIN_JWT_SECRET', 'replaced'),
    },
  },
});

I was concentrated to use subdomains, the preference of @pm74. My own preference is like this:

// modified server config
module.exports = ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  url: "https://domain.com/api", 
  admin: {
    url: "https://domain.com/admin", 
    auth: {
      secret: env('ADMIN_JWT_SECRET', 'replaced'),
    },
  },
});

Generally speaking (given I wrote the proxy documentation: https://strapi.io/documentation/developer-docs/latest/getting-started/deployment.html#optional-software-guides ) I would suggest to avoid the sub-folder proxying where possible and opt to use subdomains only.

Combining the two can result in a fairly complex proxy structure. Also splitting your Admin vs endpoint urls also could be quite confusing :thinking:

What I usually suggest is define your prod, stag, dev (you don’t usually deploy dev) infra scopes and keep them seperate:

  • Dev => Local based
  • Staging => As close to prod as possible but does not overlap
  • Prod => Least “breaking” as possible, goal is uptime and stability.

As an example on my own hobby project: GitHub - canonn-science/CAPIv2-Strapi: Canonn APIv2

I have 4 environments:

Given my project is not narrowly scoped to only accept requests from my own applications but is ran as a public API (video game: Elite Dangerous). My staging is designed to be used by people who want to test and don’t care about errors, typically external developers. Rate limits are much more lax here and permissions are a bit less restricted.

Subdomains are used to create isolated parts of an application stack, this is done to make it clear they are a separate service. And you should have as little overlap as possible between staging and production to minimize risks of pollution. In my case the user sync between prod and staging is one way. Production users get synced to staging, never in the reverse.

1 Like

Ok, I’m very lost and my configuration is not working at all. Once I put both my dev and prod env to automatically start in PM2, I get no access to create new content even connected with the superuser.
My understanding is that it thinks it is in production mode and that’s why it’s not letting me do it. It will not allow creating anything through Content Builder.

Below my configuration, and I will now try you configuration to see if it works any better.

module.exports = {
       apps: [{
           name: 'strapiProd',
           cwd: '/mnt/data/strapi/Prod',
           script: 'npm',
           args: 'start',
           env: {
             NODE_ENV: 'production',
             DATABASE_HOST: 'localhost', // database endpoint
             DATABASE_PORT: '3306',
             DATABASE_NAME: '', // DB name
             DATABASE_USERNAME: 'strapi', // your username for psql
             DATABASE_PASSWORD: '', // your password for psql
           },
         },{
           name: 'strapiDev',
           cwd: '/mnt/data/strapi/dev',
           script: 'npm',
           args: 'start',
           env: {
             NODE_ENV: 'development',
             DATABASE_HOST: 'localhost', // database endpoint
             DATABASE_PORT: '3306',
             DATABASE_NAME: '', // DB name
             DATABASE_USERNAME: '', // your username for psql
             DATABASE_PASSWORD: '', // your password for psql
           },
         },
       ],
     };

~

This is my upstream conf

/etc/nginx/conf.d/upstream.conf

Strapi server

upstream strapiProd{
server 127.0.0.1:1337;
}
upstream strapiDev{
server 127.0.0.1:1338;
}

You will need to set the permissions in production as well as dev/staging, those are stored in the database and aren’t in source control.

Can you give some examples? Because this sounds like a bug but it depends on what you mean.

First, thank you for the details. Regarding the opt for subdomains, as you said… where possible! From my point of view this opt is not quite an option, mostly the type of the project dictates what is should be!

Yes, not easy task, but again, in my case not splitting is not an option! Imagine an eCommerce site, letting the admin endpoint be publicly accessible would attract a huge volume of spam (scanners, bots, etc.). Moreover, I consider that the access to the Strapi administration panel is poorly protected. This is also the main reason why I split access and restrict ip based in the Nginx configuration, as follows:

// nginx (inside isolated admin location)
location /admin {
    Allow 123.123.123.123;
    Deny All;
    ...
}

I’m logged in with the super user and cannot create content, the server things I’m in Production mode.
If I do yarn develop it works, but I cannot get it to work with my above mentioned configuration.

Which permission are you talking about here?

Ah alright this isn’t RBAC then this is normal Strapi default. We forcefully disable the CTB (Content-Type builder) in Production and Staging. Even in development mode it will only be enabled under a very specific set of circumstances:

  • You must be using the NODE_ENV=development (the default env)
  • You must be using the strapi develop command (or yarn develop / npm run develop)
  • You cannot use a server.js file with the start syntax
  • You cannot use the CTB in any other environment

For reasons why I’ll lead you to this FAQ entry: Troubleshooting - Strapi Developer Documentation

But will post it here also:


Strapi stores model configuration files (what defines the model schema) in files such as api/restaurant/models/restaurant.settings.json. Due to how Node.js works, in order for changes to take effect, that would require Node to restart the server. This could potentially cause downtime of your production service and likewise these changes should be tracked in some kind of source control.

Generally your “flow” of development would follow the following path:

  • Development - Develop your Strapi application locally on your host machine, then push changes into source control
  • Staging - Deploy changes from source control to a “production-like” environment for testing
  • Production - If no other changes are needed, deploy into production
  • Repeat as needed, it is recommended that you properly version and test your application as you go

At this time and in the future there is no plan to allow model creating or updating while in a production environment, and there is currently no plans to move model settings into the database. There is no known nor recommended workarounds for this.

1 Like