node.jsexpresspermissionspm2mkdir

How to allow PM2 to create folders on Ubuntu Server?


I have an express app managed via PM2 deployed to an Ubuntu Server. At a certain point an endpoint receives and saves some files in this way

fs.access(path.join(__dirname, '..', 'static', 'images', 'users', userId), (error) => {
  if (error) {
    fs.mkdirSync(path.join(__dirname, '..', 'static', 'images', 'users', userId));
  }
});

But the process crashes. From the logs I can see:

0|App | node:internal/fs/utils:344
0|App |     throw err;
0|App |     ^
0|App |
0|App | Error: ENOENT: no such file or directory, mkdir '/var/www/services.my-app.it/static/images/users/Vtk7xRzSU0fmaqyViTbv5ItQTXs2'
0|App |     at Object.mkdirSync (node:fs:1334:3)
0|App |     at /var/www/services.my-app.it/dist/index.js:1:11558
0|App |     at FSReqCallback.oncomplete (node:fs:188:23) {
0|App |   errno: -2,
0|App |   syscall: 'mkdir',
0|App |   code: 'ENOENT',
0|App |   path: '/var/www/services.my-app.it/static/images/users/Vtk7xRzSU0fmaqyViTbv5ItQTXs2'
0|App | }
0|App | error Command failed with exit code 1.

It's clearly a permissions problem, however I can't figure out what is the user that needs the permission in order to solve the problem.

I tried to launch the process via pm2 start both from root and personal user, nothing changes.

▶ ll
total 312K
drwxr-xr-x   2 root  root  4.0K May  4 12:15 dist
-rw-rw-r--   1 thefe thefe 1.1K May  2 15:18 index.js
-rw-rw-r--   1 thefe thefe  440 May  2 14:55 jsconfig.json
drwxrwxr-x 485 thefe thefe  20K May  2 14:56 node_modules
-rw-rw-r--   1 thefe thefe 1.4K May  2 14:55 package.json
-rw-rw-r--   1 thefe thefe 2.3K Apr 15  2022 service-account.json
drwxrwxr-x   7 thefe thefe 4.0K Apr  9 19:59 src
drwxrwxrwx   2 root  root  4.0K May  4 09:48 static
-rw-rw-r--   1 thefe thefe 1012 May  2 14:55 webpack.config.js
-rw-rw-r--   1 thefe thefe 259K May  2 14:55 yarn.lock

Solution

  • Ok, I figured it out! It wasn't actually a permissions problem (that were configured correctly), but actually the { recursive: true } was missing from the fs.mkdirSync, and so Node wasn't able to create the subfolders. Adding that solved the problem.

    UPDATED CODE IS:

    fs.access(path.join(__dirname, '..', 'static', 'images', 'users', userId), (error) => {
      if (error) {
        fs.mkdirSync(path.join(__dirname, '..', 'static', 'images', 'users', userId), { recursive: true });
      }
    });
    

    After some researches I found a new way to do what I'm doing with new NodeJS functions. I created a utility method, credits go to this answer

    import { stat, mkdir } from 'node:fs/promises';
    
    const checkAndMakeDir = async (dir) => {
      try {
        await stat(dir);
      } catch (error) {
        if (error.code === 'ENOENT') {
          try {
            await mkdir(dir, { recursive: true });
          } catch (err) {
            console.error(err.message);
          }
        }
      }
    };
    
    export default checkAndMakeDir;