Cron job with PATH that includes PHP?

I have some cron jobs setup like this:

  services.cron = {
    enable = true;
    systemCronJobs = [
      "* * * * *      nobody    php ${domain1-source.source-code}/some_file.php"
      "* * * * *      nobody    env > /tmp/env.output"
    ];
  };

And I see mail in /var/spool/mail/nobody/new telling me that the first one there failed because php is no on the PATH. I created the second cron job to figure out what the environment looked like. It looks like this:

USER=nobody
PWD=/var/empty
HOME=/var/empty
NIX_CONF_DIR=/etc/nix
SHELL=/nix/store/xvvgw9sb8wk6d2c0j3ybn7sll67s3s4z-bash-4.4-p23/bin/bash
SHLVL=1
LOGNAME=nobody
PATH=/nix/store/az9zj4k0kdwq024b4my2gvszqm7s6b1p-system-path/bin:/nix/store/az9zj4k0kdwq024b4my2gvszqm7s6b1p-system-path/sbin
_=/nix/store/az9zj4k0kdwq024b4my2gvszqm7s6b1p-system-path/bin/env

And I guess that’s very reasonable! But how can/should I get the path to php in there? I’m using php by using services.phpfpm.

Your looking for this:

"* * * * *      nobody    ${pkgs.php}/bin/php ${domain1-source.source-code}/some_file.php"

You can customize the php with extensions etc… like you do elsewhere.

3 Likes

Thank you very much once again @aanderse ! That made it work.

I have the need to include more environment variables though. For my application I’m including environment variables like this:

            services.phpfpm.pools.${my_app}.phpEnv = {
                MYSQL_HOST = "localhost";
                MYSQL_DATABASE = "${theconfig.db}";
                MYSQL_USER = "${theconfig.user}";
                MYSQL_SOCKET = "${theconfig.socket}";
                EMAIL = "${theconfig.email}";
            };

Is there a nice way to make all the same environment variables available to my cronjob? I could imagine doing this:

"* * * * *      nobody    ENVIRONMENT_VAR1=${theconfig.var1} ENVIRONMENT_VAR2=${theconfig.var2} ${pkgs.php}/bin/php ${domain1-source.source-code}/some_file.php"

But that’s a little cumbersome.

I highly recommend using a systemd timer and service unit instead of cron jobs.

Something like this could replace your cron job:

systemd.services.myjob = {
  startAt = "*-*-* *:*:00";
  environment = {
    MYSQL_HOST = "localhost";
    MYSQL_DATABASE = "${theconfig.db}";
    MYSQL_USER = "${theconfig.user}";
    # etc...
  };
  serviceConfig = {
    Type = "oneshot":
    DynamicUser = true;
    ExecStart = "${pkgs.php}/bin/php ${domain1-source.source-code}/some_file.php"";
  };
};
2 Likes

Thank you again, @aanderse! I’m having some problems executing my script (due to include_path or something), so I tried cd’ing into the directory first. Like this:

  systemd.services.myjob = {
    startAt = "*-*-* *:*:00";
    environment = theconfig.environment;
    serviceConfig = {
      Type = "oneshot";
      DynamicUser = true;
      ExecStart = "cd ${domain1-source.source-code}/path_to_crons/ && ${pkgs.php}/bin/php ${domain1-source.source-code}/path_to_crons/some_file.php";
    };
  };

But then I get this (from journalctl):

Sep 02 18:59:00 myvps systemd[519191]: myjob.service: Failed to locate executable cd: No such file or directory

I tried the env trick again:

  systemd.services.myjob = {
    startAt = "*-*-* *:*:00";
    #environment = theconfig.environment;
    serviceConfig = {
      Type = "oneshot";
      DynamicUser = true;
      ExecStart = "env > /tmp/env.output";
    };
  };

While actually commenting out my own environment there, and the output was:

Sep 02 19:20:06 myvps systemd[520630]: myjob.service: Failed to locate executable env: No such file or directory

I’ve googled a bit to try to understand what type of environment the systemd service will run in, but I’m not getting any smarter. Do you have any pointers?

Sure. You’re looking for serviceConfig.WorkingDirectory = "${domain1-source.source-code}/path_to_crons";.

If you want to see your full environment for a systemd service you can run systemctl cat myjob.service.

Keep in mind that NixOS tries to sandbox as much as possible, so any commands your php scripts might need won’t be available unless you explicitly make it available.

1 Like

Thanks again @aanderse ! My “crons” are now working great :slightly_smiling_face:

1 Like