Run php script as daemon process

161,455

Solution 1

You could start your php script from the command line (i.e. bash) by using

nohup php myscript.php &

the & puts your process in the background.

Edit:
Yes, there are some drawbacks, but not possible to control? That's just wrong.
A simple kill processid will stop it. And it's still the best and simplest solution.

Solution 2

Another option is to use Upstart. It was originally developed for Ubuntu (and comes packaged with it by default), but is intended to be suitable for all Linux distros.

This approach is similar to Supervisord and daemontools, in that it automatically starts the daemon on system boot and respawns on script completion.

How to set it up:

Create a new script file at /etc/init/myphpworker.conf. Here is an example:

# Info
description "My PHP Worker"
author      "Jonathan"

# Events
start on startup
stop on shutdown

# Automatically respawn
respawn
respawn limit 20 5

# Run the script!
# Note, in this example, if your PHP script returns
# the string "ERROR", the daemon will stop itself.
script
    [ $(exec /usr/bin/php -f /path/to/your/script.php) = 'ERROR' ] && ( stop; exit 1; )
end script

Starting & stopping your daemon:

sudo service myphpworker start
sudo service myphpworker stop

Check if your daemon is running:

sudo service myphpworker status

Thanks

A big thanks to Kevin van Zonneveld, where I learned this technique from.

Solution 3

With new systemd you can create a service.

You must create a file or a symlink in /etc/systemd/system/, eg. myphpdaemon.service and place content like this one, myphpdaemon will be the name of the service:

[Unit]
Description=My PHP Daemon Service
#May your script needs MySQL or other services to run, eg. MySQL Memcached
Requires=mysqld.service memcached.service 
After=mysqld.service memcached.service

[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/myphpdaemon.pid
ExecStart=/usr/bin/php -f /srv/www/myphpdaemon.php arg1 arg2> /dev/null 2>/dev/null
#ExecStop=/bin/kill -HUP $MAINPID #It's the default you can change whats happens on stop command
#ExecReload=/bin/kill -HUP $MAINPID
KillMode=process

Restart=on-failure
RestartSec=42s

StandardOutput=null #If you don't want to make toms of logs you can set it null if you sent a file or some other options it will send all PHP output to this one.
StandardError=/var/log/myphpdaemon.log
[Install]
WantedBy=default.target

You will be able to start, get status, restart and stop the services using the command

systemctl <start|status|restart|stop|enable> myphpdaemon

You can use the PHP native server using php -S 127.0.0.1:<port> or run it as a script. Using a PHP script you should have a kind of "forever loop" to continue running.

<?php
gc_enable();//
while (!connection_aborted() || PHP_SAPI == "cli") {
  
  //Code Logic
  
  //sleep and usleep could be useful
    if (PHP_SAPI == "cli") {
        if (rand(5, 100) % 5 == 0) {
            gc_collect_cycles(); //Forces collection of any existing garbage cycles
        }
    }
}

Working example:

[Unit]
Description=PHP APP Sync Service
Requires=mysqld.service memcached.service
After=mysqld.service memcached.service

[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/php_app_sync.pid
ExecStart=/bin/sh -c '/usr/bin/php -f /var/www/app/private/server/cron/app_sync.php  2>&1 > /var/log/app_sync.log'
KillMode=mixed

Restart=on-failure
RestartSec=42s

[Install]
WantedBy=default.target

If your PHP routine should be executed once in a cycle (like a diggest) you may use a shell or bash script to be invoked into systemd service file instead of PHP directly, for example:

#!/usr/bin/env bash
script_path="/app/services/"

while [ : ]
do
#    clear
    php -f "$script_path"${1}".php" fixedparameter ${2}  > /dev/null 2>/dev/null
    sleep 1
done

If you chose these option you should change the KillMode to mixed to processes, bash(main) and PHP(child) be killed.

ExecStart=/app/phpservice/runner.sh phpfile parameter  > /dev/null 2>/dev/null
KillMode=process

This method also is effective if you're facing a memory leak.

Note: Every time that you change your "myphpdaemon.service" you must run `systemctl daemon-reload', but do worry if you do not do, it will be alerted when is needed.

Solution 4

If you can - grab a copy of Advanced Programming in the UNIX Environment. The entire chapter 13 is devoted to daemon programming. Examples are in C, but all the function you need have wrappers in PHP (basically the pcntl and posix extensions).

In a few words - writing a daemon (this is posible only on *nix based OS-es - Windows uses services) is like this:

  1. Call umask(0) to prevent permission issues.
  2. fork() and have the parent exit.
  3. Call setsid().
  4. Setup signal processing of SIGHUP (usually this is ignored or used to signal the daemon to reload its configuration) and SIGTERM (to tell the process to exit gracefully).
  5. fork() again and have the parent exit.
  6. Change the current working dir with chdir().
  7. fclose() stdin, stdout and stderr and don't write to them. The corrrect way is to redirect those to either /dev/null or a file, but I couldn't find a way to do it in PHP. It is possible when you launch the daemon to redirect them using the shell (you'll have to find out yourself how to do that, I don't know :).
  8. Do your work!

Also, since you are using PHP, be careful for cyclic references, since the PHP garbage collector, prior to PHP 5.3, has no way of collecting those references and the process will memory leak, until it eventually crashes.

Solution 5

I run a large number of PHP daemons.

I agree with you that PHP is not the best (or even a good) language for doing this, but the daemons share code with the web-facing components so overall it is a good solution for us.

We use daemontools for this. It is smart, clean and reliable. In fact we use it for running all of our daemons.

You can check this out at http://cr.yp.to/daemontools.html.

EDIT: A quick list of features.

  • Automatically starts the daemon on reboot
  • Automatically restart dameon on failure
  • Logging is handled for you, including rollover and pruning
  • Management interface: 'svc' and 'svstat'
  • UNIX friendly (not a plus for everyone perhaps)
Share:
161,455

Related videos on Youtube

Beier
Author by

Beier

Updated on February 09, 2022

Comments

  • Beier
    Beier about 2 years

    I need to run a php script as daemon process (wait for instructions and do stuff). cron job will not do it for me because actions need to be taken as soon as instruction arrives. I know PHP is not really the best option for daemon processes due to memory management issues, but due to various reasons I have to use PHP in this case. I came across a tool by libslack called Daemon (http://libslack.org/daemon) it seems to help me manage daemon processes, but there hasn't been any updates in the last 5 years, so I wonder if you know some other alternatives suitable for my case. Any information will be really appreciated.

  • Beier
    Beier over 14 years
    Thanks for the info. It looks like libslack's daemon program pretty much does all the prep work like you mentioned. I think for now I'll stick with it until I find other good alternatives.
  • Cameron
    Cameron over 14 years
    If the terminal exists the process will NOT exit. That's why the "nohup" command is there. I have been using a PHP script as daemon on all servers like just this for years now. There might be better solution, but this is the quickest one.
  • Phil Wallach
    Phil Wallach over 14 years
    This will not restart the daemon if it fails, and there is no easy way to manage the daemon at all.
  • Simian
    Simian about 13 years
    I agree with what's been said here-- this is a bad solution. You should create an init script for a couple of reasons: 1) Init script is launched automatically on startup 2) You can manage the daemon with start/stop/restart commands. Here is an example from servefault: serverfault.com/questions/229759/…
  • ThiefMaster
    ThiefMaster over 10 years
    Found this post, expected code to copy&paste into a crappy old application that fails to close stdin etc., was disappointed. :p
  • nourdine
    nourdine about 10 years
    hey guys ... it seems to me nohup and & does the same very thing: detaching the launched process from the current intance of the shell. Why do I need them both? Can I not just do php myscript.php & or nohup myscript.php?? Thanks
  • Alix Axel
    Alix Axel about 10 years
    Could you provide a similar supervisord config?
  • Thomas
    Thomas almost 10 years
    To add some more clarity to this. nohup will make the script ignore a SIGTERM it will obviously still die with a KILL. You do need both, the nohup will make sure that if the parent process dies the script isn't taken with it, the & will make sure it will be pushed to the background as soon as it's called. In short this is a nice though hacky way to demonize and in PHP you're pretty much dependent on hacky ways for this use-case.
  • Manuel
    Manuel almost 10 years
    loving this. Just wondering, is it possible to have multiple concurrent workers? I just have the problem that one worker isn't enough any more.
  • Jonathan
    Jonathan almost 10 years
    @Manuel That's above my expertise level, sorry!
  • TheFox
    TheFox almost 10 years
    Why (5) fork() again?
  • slier
    slier over 9 years
    will this be automatically running on system startup?
  • Matt Sich
    Matt Sich over 9 years
    Sudo "service myphpworker start" didn't work for me. I used "sudo start myphpworker" and it works perfectly
  • Gautam Sharma
    Gautam Sharma about 9 years
    Worked for me - great job!
  • Pradeepta
    Pradeepta almost 9 years
    While executing "sudo service myphpworker start" its showing myphpworker: unrecognized service.
  • ckm
    ckm almost 9 years
    @Pradeepta That's because there is an error in the post - I'm not exactly sure what (and haven't tested this), but I think that sudo service myphpworker start/stop/status only works with services that are in /etc/init.d not upstart services. @matt-sich seems to have uncovered the correct syntax. Another option is to use Gearman or Resque, which allows background processing & deamonization.
  • php_nub_qq
    php_nub_qq over 8 years
    I think this needs more upvotes! How can I start the script with sudo though, as in $(exec sudo /usr/bin/php ... this clearly doesn't work but my PHP script needs su rights, how can I do that?
  • Ghedipunk
    Ghedipunk over 8 years
    For future readers asking why to fork twice: stackoverflow.com/questions/881388/… -- TL;DR: Prevents zombies.
  • Mischa
    Mischa about 8 years
    If the script writes to stdout (via echo or var_dump) then you can catch this information with a log file like this: nohup php myscript.php > myscript.log &
  • tonix
    tonix almost 8 years
    @Ghedipunk I read some answers at the link you posted, and I would say that a double fork is also used to prevent the second child to open a TTY terminal. You will prevent zombies too if you fork once cause the first child will become a child of init and will be cleaned up upon its execution by init's periodical wait system calls.
  • DavidScherer
    DavidScherer over 7 years
    @Manuel, you would want the main daemon to either fork() or spawn additional instances of itself or another script that runs as children. I generally have my main daemon managing multiple children when necessary. Because PHP doesn't allow these processes to really 'talk,' you have to get creative. I've had success using sockets, files, and databases for this purpose but sockets have been the easiest to work with. You simply pass a port to the child to connect to or have the child connect to a standard configurable port and report its PID to the parent.
  • Kzqai
    Kzqai about 7 years
    Ubuntu itself is moving to using systemd instead of upstart: zdnet.com/article/after-linux-civil-war-ubuntu-to-adopt-syst‌​emd
  • Kzqai
    Kzqai about 7 years
    Also installable from the repositories, e.g. in apt!
  • Gergely Lukacsy
    Gergely Lukacsy almost 7 years
    Underrated answer. You have my +1.
  • Justin
    Justin over 6 years
    Awesome. Wish we could heart answers too because this should not be buried on this page.
  • Boy
    Boy over 6 years
    Log files for each deamon are at this location: /var/log/upstart/
  • Boy
    Boy over 6 years
    @Jonathan How do I pass an argument to the .conf script? I would like that its remembered somewhere so that it uses those same parameters when the deamon automatically restarts. My goal is this: exec /usr/bin/php -f /path/to/your/script.php SOME_PARAM1 SOME_PARAM2
  • ScipioAfricanus
    ScipioAfricanus over 6 years
    Hey, I am trying to do systemctl stop servicename but the php file keeps outputting to file .. I am working off of the "working example" file.
  • LeonanCarvalho
    LeonanCarvalho over 6 years
    @ScipioAfricanus Maybe your are with ignoring user abort enabled in your php.
  • ScipioAfricanus
    ScipioAfricanus about 6 years
    Hey, thanks for the tip but .. service still doesnt stop. I copied the php code verbatum, and when I do ps -ef | grep php this is what I get user 14020 1 0 13:03 ? 00:00:00 /bin/sh -c /usr/bin/php -f /app/folder/test.php 2>&1 > /app/folder/log.out user 14021 14020 0 13:03 ? 00:00:00 /usr/bin/php -f /app/folder/test.php The only change I made to the pid controller is I set user and group to some value. I tried it with root first, ofc, no difference.
  • LeonanCarvalho
    LeonanCarvalho about 6 years
    You should check systemctl status <your_service_name> -l output, it will give you a clue whats happening.
  • symcbean
    symcbean about 6 years
    -1: the advice here is poor and will result in a process not properly isolated from the controlling terminal, and giving rise to zombies.
  • Leandro Bardelli
    Leandro Bardelli over 5 years
    use this answer for ubuntu/debian and/or 2018+
  • Leandro Bardelli
    Leandro Bardelli over 5 years
    For ubuntu, mysqld is mysql, and memcache must be installed before. Also is more practical use a symlink to something like /var/www/daemons/myservice.service
  • LeonanCarvalho
    LeonanCarvalho over 5 years
    @LeandroTupone The MySQL and Memcached was a demonstration of how to use service dependencies it's not necessary.
  • Leandro Bardelli
    Leandro Bardelli over 5 years
    Im using mysqli in php, dont need mysql service reference ?
  • LeonanCarvalho
    LeonanCarvalho over 5 years
    @LeandroTupone You should refer any services that your PHP server depends on, just to prevent failure but it's not necessary.
  • Leandro Bardelli
    Leandro Bardelli over 5 years
    thanks for all your help. How could you put this as autostart?
  • LeonanCarvalho
    LeonanCarvalho over 5 years
    @LeandroTupone systemctl enable phpdaemon.service
  • Leandro Bardelli
    Leandro Bardelli over 5 years
    I've several issues to stop the daemon, even with execStop, php will run forever and wont stop, multiple scripts will run at the same time with concurrence problems
  • LeonanCarvalho
    LeonanCarvalho over 5 years
    @LeandroTupone If your service start sub-scripts you should try other configuration for KillMode and ExecStop freedesktop.org/software/systemd/man/systemd.kill.html
  • spice
    spice about 5 years
    This should really replace the accepted answer seeing as it's now 2019.
  • Armedin Kuka
    Armedin Kuka about 4 years
    I am surprised how this is not the accepted answer... Best solution as of 2020
  • zanderwar
    zanderwar over 3 years
    sudo yum install -y supervisor uses systemd
  • Pavel A.
    Pavel A. about 2 years
    PIDFile= not needed if used Type=simple. freedesktop.org/software/systemd/man/…
  • Tobia
    Tobia about 2 years
    Is it possibile to use this solution for a multithread job?
  • LeonanCarvalho
    LeonanCarvalho about 2 years
    @tobia sure, using pcntl_fork
  • Tuhin
    Tuhin almost 2 years
    For linux systems running systemd, I would say this is the best answer. you've got my upvote for this.