{"id":3505,"date":"2025-12-27T18:08:02","date_gmt":"2025-12-27T15:08:02","guid":{"rendered":"https:\/\/www.dchost.com\/blog\/why-background-jobs-matter-so-much-on-a-vps\/"},"modified":"2026-11-04T19:54:08","modified_gmt":"2026-11-04T16:54:08","slug":"laravel-queues-supervisor-systemd-pm2","status":"publish","type":"post","link":"https:\/\/www.dchost.com\/blog\/en\/laravel-queues-supervisor-systemd-pm2\/","title":{"rendered":"Laravel Queues: Supervisor, systemd and PM2"},"content":{"rendered":"<div class=\"dchost-blog-content-wrapper\"><p>Background jobs let a web application move suitable work outside the HTTP request: order emails, invoices, reports, media processing, indexing and integrations. This can shorten response time and make concurrency easier to control; queueing does not remove the work or guarantee faster completion. Database queries, application code, external services and resource pressure still need diagnosis. On a VPS, reliable processing depends on both the application and the processes that consume its queues.<\/p>\n<p>This guide covers Laravel queues on a VPS, Supervisor and systemd for PHP workers, and PM2 for separate Node.js consumers. The code targets Laravel 13 with PHP 8.3 or later. Debian\/Ubuntu paths and the www-data account are examples: verify the installed CLI binary, extensions, user and permissions. Treat the process-manager configurations as alternatives; one manager should own each worker group.<\/p>\n<p>Hosting support for persistent workers varies by plan. A VPS gives you control over services and scheduling, but boot startup, recovery and monitoring must be configured. A panel or management service may provide some of these functions; do not assume an unmanaged worker will restart merely because the server is running.<\/p>\n<p>Background jobs give you several concrete benefits:<\/p>\n<ul>\n<li><strong>Response time:<\/strong> Return an accepted or pending state after successful enqueueing where appropriate. Do not report payment, delivery or another external action as completed merely because it was queued.<\/li>\n<li><strong>Recovery:<\/strong> Failed work can be retried when backend persistence and retry policies support it; retries may repeat external effects.<\/li>\n<li><strong>Resource control:<\/strong> Limit concurrency, budget CPU\/RAM and schedule suitable heavy jobs away from peak periods.<\/li>\n<li><strong>Scaling:<\/strong> Workers can be scaled separately from HTTP processes, while still depending on shared broker, database, storage and network capacity.<\/li>\n<\/ul>\n<p>If you\u2019re still deciding between shared hosting and a VPS for your Laravel or PHP app, it\u2019s worth reading our detailed comparison on <a href=\"https:\/\/www.dchost.com\/blog\/en\/shared-hosting-vs-vps-for-laravel-and-php-frameworks-queues-schedulers-and-cache\/\">when Laravel really needs a VPS for queues, schedulers and cache<\/a>. Once you\u2019re on a VPS, the rest of this article will help you put those queues on solid rails.<\/p>\n<div id=\"toc_container\" role=\"navigation\" aria-label=\"Table of Contents\" data-nosnippet class=\"toc_transparent no_bullets toc_numbered toc_title_center\"><p class=\"toc_title\">\u0130\u00e7indekiler<\/p><ul class=\"toc_list\"><li><a href=\"#Laravel_Queues_on_a_VPS_Core_Concepts\"><span class=\"toc_number toc_depth_1\">1.<\/span> Laravel Queues on a VPS: Core Concepts<\/a><\/li><li><a href=\"#Option_1_Running_Laravel_Queues_with_Supervisor\"><span class=\"toc_number toc_depth_1\">2.<\/span> Option 1: Running Laravel Queues with Supervisor<\/a><ul><li><a href=\"#When_Supervisor_Makes_Sense\"><span class=\"toc_number toc_depth_2\">2.1.<\/span> When Supervisor Makes Sense<\/a><\/li><li><a href=\"#Basic_Supervisor_Setup_for_Laravel\"><span class=\"toc_number toc_depth_2\">2.2.<\/span> Basic Supervisor Setup for Laravel<\/a><\/li><li><a href=\"#Key_Settings_You_Should_Think_About\"><span class=\"toc_number toc_depth_2\">2.3.<\/span> Key Settings You Should Think About<\/a><\/li><li><a href=\"#Pros_and_Cons_of_Supervisor\"><span class=\"toc_number toc_depth_2\">2.4.<\/span> Pros and Cons of Supervisor<\/a><\/li><\/ul><\/li><li><a href=\"#Option_2_Using_systemd_Services_for_Queue_Workers\"><span class=\"toc_number toc_depth_1\">3.<\/span> Option 2: Using systemd Services for Queue Workers<\/a><ul><li><a href=\"#Why_systemd_Is_Attractive_on_a_VPS\"><span class=\"toc_number toc_depth_2\">3.1.<\/span> Why systemd Is Attractive on a VPS<\/a><\/li><li><a href=\"#Creating_a_systemd_Unit_for_Laravel_Queues\"><span class=\"toc_number toc_depth_2\">3.2.<\/span> Creating a systemd Unit for Laravel Queues<\/a><\/li><li><a href=\"#Scaling_with_systemd_Templates\"><span class=\"toc_number toc_depth_2\">3.3.<\/span> Scaling with systemd Templates<\/a><\/li><li><a href=\"#Pros_and_Cons_of_systemd\"><span class=\"toc_number toc_depth_2\">3.4.<\/span> Pros and Cons of systemd<\/a><\/li><\/ul><\/li><li><a href=\"#Option_3_PM2_for_Nodejs_and_Mixed_Stacks\"><span class=\"toc_number toc_depth_1\">4.<\/span> Option 3: PM2 for Node.js and Mixed Stacks<\/a><ul><li><a href=\"#PM2_Basics\"><span class=\"toc_number toc_depth_2\">4.1.<\/span> PM2 Basics<\/a><\/li><li><a href=\"#PM2_with_Laravel_Nodejs\"><span class=\"toc_number toc_depth_2\">4.2.<\/span> PM2 with Laravel + Node.js<\/a><\/li><li><a href=\"#Pros_and_Cons_of_PM2\"><span class=\"toc_number toc_depth_2\">4.3.<\/span> Pros and Cons of PM2<\/a><\/li><\/ul><\/li><li><a href=\"#Horizon_and_Advanced_Laravel_Queue_Management\"><span class=\"toc_number toc_depth_1\">5.<\/span> Horizon and Advanced Laravel Queue Management<\/a><ul><li><a href=\"#How_Horizon_Fits_into_the_Picture\"><span class=\"toc_number toc_depth_2\">5.1.<\/span> How Horizon Fits into the Picture<\/a><\/li><\/ul><\/li><li><a href=\"#Monitoring_Scaling_and_Troubleshooting_Queues_on_a_VPS\"><span class=\"toc_number toc_depth_1\">6.<\/span> Monitoring, Scaling and Troubleshooting Queues on a VPS<\/a><ul><li><a href=\"#Monitoring_Worker_Health\"><span class=\"toc_number toc_depth_2\">6.1.<\/span> Monitoring Worker Health<\/a><\/li><li><a href=\"#Capacity_Planning_for_Workers\"><span class=\"toc_number toc_depth_2\">6.2.<\/span> Capacity Planning for Workers<\/a><\/li><li><a href=\"#Common_Queue_Problems_and_How_to_Fix_Them\"><span class=\"toc_number toc_depth_2\">6.3.<\/span> Common Queue Problems and How to Fix Them<\/a><\/li><\/ul><\/li><li><a href=\"#Choosing_Supervisor_systemd_or_PM2_for_Your_VPS\"><span class=\"toc_number toc_depth_1\">7.<\/span> Choosing Supervisor, systemd or PM2 for Your VPS<\/a><ul><li><a href=\"#Scenario_1_Pure_Laravel_App_Small_to_Medium_Scale\"><span class=\"toc_number toc_depth_2\">7.1.<\/span> Scenario 1: Pure Laravel App, Small to Medium Scale<\/a><\/li><li><a href=\"#Scenario_2_Laravel_Nodejs_Mixed_Stack\"><span class=\"toc_number toc_depth_2\">7.2.<\/span> Scenario 2: Laravel + Node.js Mixed Stack<\/a><\/li><li><a href=\"#Scenario_3_Multiple_Apps_on_One_VPS\"><span class=\"toc_number toc_depth_2\">7.3.<\/span> Scenario 3: Multiple Apps on One VPS<\/a><\/li><li><a href=\"#Security_and_Stability_Considerations\"><span class=\"toc_number toc_depth_2\">7.4.<\/span> Security and Stability Considerations<\/a><\/li><\/ul><\/li><li><a href=\"#Putting_It_All_Together_on_Your_dchostcom_VPS\"><span class=\"toc_number toc_depth_1\">8.<\/span> Putting It All Together on Your dchost.com VPS<\/a><\/li><li><a href=\"#Primary_Technical_References\"><span class=\"toc_number toc_depth_1\">9.<\/span> Primary Technical References<\/a><\/li><\/ul><\/div>\n<h2><span id=\"Laravel_Queues_on_a_VPS_Core_Concepts\">Laravel Queues on a VPS: Core Concepts<\/span><\/h2>\n<p>Before we dive into Supervisor, systemd or PM2, it\u2019s important to be clear on what Laravel itself is doing. Laravel provides three main pieces in the queue story:<\/p>\n<ul>\n<li><strong>Queue backends:<\/strong> Laravel supports database, Redis, Beanstalkd and SQS, among other drivers. RabbitMQ integration requires a compatible package. The sync driver runs immediately in the calling process and can be intentional, including production; it does not provide asynchronous processing.<\/li>\n<li><strong>Workers:<\/strong> Long-running PHP processes execute Laravel jobs from an asynchronous connection. A Node.js consumer needs its own compatible queue protocol.<\/li>\n<li><strong>Horizon:<\/strong> Optional management and metrics for Laravel Redis queues. The Laravel 13 documentation excludes Redis Cluster support. A process manager still needs to supervise Horizon itself.<\/li>\n<\/ul>\n<p>First distinguish the Laravel queue connection from the underlying Redis connection. This standalone Redis example selects the redis queue connection, which uses a named Redis connection called queue. Set these environment values:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">QUEUE_CONNECTION=redis\nREDIS_QUEUE_CONNECTION=queue\nREDIS_QUEUE=default\nREDIS_QUEUE_RETRY_AFTER=90\nREDIS_QUEUE_DB=3\n<\/code><\/pre>\n<p>Add this queue entry inside the redis array in config\/database.php, keeping the existing client and options configuration. REDIS_QUEUE_DB is a custom variable: it works because this fragment reads it. Redis access, credentials and the selected PhpRedis or Predis client must already be configured:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">&#039;queue&#039; =&gt; [\n    &#039;host&#039; =&gt; env(&#039;REDIS_HOST&#039;, &#039;127.0.0.1&#039;),\n    &#039;username&#039; =&gt; env(&#039;REDIS_USERNAME&#039;),\n    &#039;password&#039; =&gt; env(&#039;REDIS_PASSWORD&#039;),\n    &#039;port&#039; =&gt; env(&#039;REDIS_PORT&#039;, &#039;6379&#039;),\n    &#039;database&#039; =&gt; env(&#039;REDIS_QUEUE_DB&#039;, &#039;3&#039;),\n],\n<\/code><\/pre>\n<p>Match connections.redis in config\/queue.php to the following:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">&#039;redis&#039; =&gt; [\n    &#039;driver&#039; =&gt; &#039;redis&#039;,\n    &#039;connection&#039; =&gt; env(&#039;REDIS_QUEUE_CONNECTION&#039;, &#039;default&#039;),\n    &#039;queue&#039; =&gt; env(&#039;REDIS_QUEUE&#039;, &#039;default&#039;),\n    &#039;retry_after&#039; =&gt; (int) env(&#039;REDIS_QUEUE_RETRY_AFTER&#039;, 90),\n    &#039;block_for&#039; =&gt; null,\n    &#039;after_commit&#039; =&gt; true,\n],\n<\/code><\/pre>\n<p>DB 3 provides a namespace, not separate memory, eviction, persistence or failure isolation. Choose queue retention and capacity policies deliberately; separate instances may be needed when cache and queue policies differ. This is not a Redis Cluster example. Avoid block_for=0 if an indefinitely blocked empty queue would delay signal handling. Run the PHP CLI worker with:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">\/usr\/bin\/php \/var\/www\/myapp\/artisan queue:work redis --queue=default --sleep=1 --tries=3 --backoff=10 --timeout=60 --max-time=3600\n<\/code><\/pre>\n<p>&#8211;tries=3 means at most three attempts including the first, not three additional retries; job-level settings may override the worker. Releases and middleware can consume attempts. &#8211;backoff=10 is the retry delay. The worker\/job timeout must be several seconds shorter than retry_after: this example uses 60 and 90 seconds. PHP CLI needs PCNTL for the timeout and signal behavior described here, and external I\/O must have its own shorter connection\/request timeouts. &#8211;max-time=3600 and &#8211;max-jobs recycle a worker between jobs; they do not interrupt a long job at that threshold.<\/p>\n<p>Keep payloads small. Laravel\u2019s Queueable trait includes model serialization support: an Eloquent model is represented by its identity and reloaded with its loaded relationships when handled. An ID is often enough; do not assume the job has a frozen copy of the original model data. For example, generate SendOrderEmail, then use this class:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">php artisan make:job SendOrderEmail\n<\/code><\/pre>\n<pre class=\"language-php line-numbers\"><code class=\"language-php\">&lt;?php\n\nnamespace App\\Jobs;\n\nuse App\\Mail\\OrderCreatedMail;\nuse App\\Models\\Order;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Foundation\\Queue\\Queueable;\nuse Illuminate\\Support\\Facades\\Mail;\n\nclass SendOrderEmail implements ShouldQueue\n{\n    use Queueable;\n\n    public function __construct(public int $orderId) {}\n\n    public function handle(): void\n    {\n        $order = Order::find($this-&gt;orderId);\n\n        if (! $order) {\n            return;\n        }\n\n        Mail::to($order-&gt;customer_email)-&gt;send(new OrderCreatedMail($order));\n    }\n}\n<\/code><\/pre>\n<p>App\\Models\\Order, customer_email and App\\Mail\\OrderCreatedMail are application dependencies. This example deliberately skips a missing order and is <strong>not idempotent<\/strong>: a retry can send a second email if the provider accepted the first before the worker stopped. Decide whether skipping is a valid business rule, and use supported provider idempotency or durable dispatch state with reconciliation where needed. From a file importing App\\Jobs\\SendOrderEmail, dispatch an existing $order after commit:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">SendOrderEmail::dispatch($order-&gt;id)-&gt;onQueue(&#039;mail&#039;)-&gt;afterCommit();\n<\/code><\/pre>\n<p>afterCommit() prevents the worker from reading an uncommitted order; it does not make the database commit and external queue write atomic. A durable outbox can address delivery gaps where the business requires it. Configure failed-job storage, inspect permanent failures and verify external effects before manually retrying.<\/p>\n<p>The dispatch example targets mail. The command above consumes default only; run the intended worker with &#8211;queue=mail or use the mail template instance below to consume those email jobs.<\/p>\n<p>With Horizon installed and configured for its assigned Redis queues, the alternative long-running entry point is:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">php artisan horizon<\/code><\/pre>\n<p>These commands are long-running. An ordinary SSH shell is not a service lifecycle: terminal closure, crashes and deployments need deliberate handling. A process manager can:<\/p>\n<ul>\n<li>Start the workers on boot<\/li>\n<li>Restart them on failure<\/li>\n<li>Optionally, limit memory\/CPU or number of restarts<\/li>\n<li>Provide logs or integrate with your logging system<\/li>\n<\/ul>\n<p>For a complete Laravel production stack beyond just queues, see the steps in our guide on <a href=\"https:\/\/www.dchost.com\/blog\/en\/laravel-vps-deployment-nginx-php-fpm-horizon\/\">deploying Laravel on a VPS with Nginx, PHP-FPM and Horizon<\/a>. Here we\u2019ll zoom in specifically on the background job side.<\/p>\n<h2><span id=\"Option_1_Running_Laravel_Queues_with_Supervisor\">Option 1: Running Laravel Queues with Supervisor<\/span><\/h2>\n<p><strong>Supervisor<\/strong> is a classic process control system for UNIX-like operating systems. It\u2019s widely used in PHP\/Laravel communities because it\u2019s simple, battle-tested, and available in most distribution repositories.<\/p>\n<h3><span id=\"When_Supervisor_Makes_Sense\">When Supervisor Makes Sense<\/span><\/h3>\n<p>Supervisor is a great fit when:<\/p>\n<ul>\n<li>You\u2019re on a typical Linux VPS (Ubuntu, Debian, AlmaLinux, Rocky Linux, etc.).<\/li>\n<li>You mostly run PHP workers (Laravel queue workers, Horizon, scheduled consumers).<\/li>\n<li>You want a simple, readable config file per queue without learning the full depth of systemd.<\/li>\n<\/ul>\n<p>Supervisor suits teams that want readable process groups and are prepared to maintain its daemon and logs. Its suitability is an operational choice, not a fixed project-size threshold.<\/p>\n<h3><span id=\"Basic_Supervisor_Setup_for_Laravel\">Basic Supervisor Setup for Laravel<\/span><\/h3>\n<p>Assuming a typical Ubuntu or Debian VPS:<\/p>\n<ol>\n<li>On Debian\/Ubuntu, install Supervisor with the package manager:<\/li>\n<\/ol>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">sudo apt update\nsudo apt install supervisor\n<\/code><\/pre>\n<p>Save the program in \/etc\/supervisor\/conf.d\/myapp-queue.conf if that directory is included by the active supervisord.conf. Other distributions may use different packages or paths. Ensure \/var\/www\/myapp, \/usr\/bin\/php, the www-data account and \/var\/log\/supervisor exist with suitable access.<\/p>\n<p>A common configuration looks like this:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">[program:myapp-queue-default]\nprocess_name=%(program_name)s_%(process_num)02d\ncommand=\/usr\/bin\/php \/var\/www\/myapp\/artisan queue:work redis --queue=default --sleep=1 --tries=3 --backoff=10 --timeout=60 --max-time=3600\ndirectory=\/var\/www\/myapp\nuser=www-data\nnumprocs=3\nautostart=true\nautorestart=true\nstartsecs=5\nstartretries=3\nstopsignal=TERM\nstopasgroup=false\nkillasgroup=true\nstopwaitsecs=75\nredirect_stderr=true\nstdout_logfile=\/var\/log\/supervisor\/%(program_name)s_%(process_num)02d.log\nstdout_logfile_maxbytes=10MB\nstdout_logfile_backups=5\n<\/code><\/pre>\n<p>Read the configuration first. If reread reports no error, update applies added or changed groups and can restart affected workers, so use it during a controlled deployment:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">sudo supervisorctl reread\nsudo supervisorctl update\nsudo supervisorctl status\n<\/code><\/pre>\n<h3><span id=\"Key_Settings_You_Should_Think_About\">Key Settings You Should Think About<\/span><\/h3>\n<ul>\n<li><strong>numprocs and process_name:<\/strong> Three sample processes each receive a unique name. Three is illustrative, not a sizing recommendation. Choose concurrency from memory, CPU, database connections and API limits.<\/li>\n<li><strong>Stop behavior:<\/strong> stopasgroup=false sends TERM to the PHP parent first so its PCNTL handler can finish the current job. After 75 seconds, killasgroup=true permits KILL of the process group. Manage child lifecycles and fit job\/I\/O deadlines within the budget; detached processes and external effects are not made safe by this setting.<\/li>\n<li><strong>Restart behavior:<\/strong> autorestart=true also restarts successful exits such as &#8211;max-time recycling. startsecs and startretries handle startup failures; alert on FATAL.<\/li>\n<li><strong>Identity:<\/strong> user changes the process account with setuid; it does not start a login shell or automatically set HOME correctly. Provide any required environment explicitly.<\/li>\n<li><strong>Logs:<\/strong> Each process has a distinct rotating stdout file. Supervisor handles its size-based rotation; do not let another rotator manage the same file. Laravel application logs need separate retention. See the <a href=\"https:\/\/www.dchost.com\/blog\/en\/vps-disk-full-errors-logrotate\/\">VPS disk and logrotate guide<\/a> for other file logs.<\/li>\n<\/ul>\n<h3><span id=\"Pros_and_Cons_of_Supervisor\">Pros and Cons of Supervisor<\/span><\/h3>\n<ul>\n<li><strong>Pros:<\/strong>\n<ul>\n<li>Very easy to read and edit configs<\/li>\n<li>Great for teams coming from shared hosting or panel-based environments<\/li>\n<li>Good control over multiple processes and groups<\/li>\n<\/ul>\n<\/li>\n<li><strong>Cons:<\/strong>\n<ul>\n<li>One more daemon to manage on the VPS<\/li>\n<li>Less integrated with the OS than systemd (for resource limits, dependencies, etc.)<\/li>\n<li>Another layer of logging to keep an eye on<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2><span id=\"Option_2_Using_systemd_Services_for_Queue_Workers\">Option 2: Using systemd Services for Queue Workers<\/span><\/h2>\n<p>If your distribution uses systemd, it can directly manage Laravel workers, restart policies, cgroups and journal logs. Use this service instead of the Supervisor program for the same worker group. The choice should reflect the team\u2019s operating model rather than an assumed difference between small and large applications.<\/p>\n<h3><span id=\"Why_systemd_Is_Attractive_on_a_VPS\">Why systemd Is Attractive on a VPS<\/span><\/h3>\n<p>Systemd brings several advantages:<\/p>\n<ul>\n<li><strong>No extra dependency:<\/strong> It\u2019s already PID 1 on most distributions.<\/li>\n<li><strong>Strong restart policies:<\/strong> Built-in support for restart throttling, delays, and failure tracking.<\/li>\n<li><strong>Resource controls:<\/strong> You can limit memory, CPU, number of processes, etc. per service.<\/li>\n<li><strong>Unified logs via journal:<\/strong> All logs can go through <code>journald<\/code>, which simplifies centralized logging.<\/li>\n<\/ul>\n<p>If you\u2019re curious about scheduling with systemd (as an alternative to cron) for periodic jobs, we\u2019ve covered it in our article on <a href=\"https:\/\/www.dchost.com\/blog\/en\/cron-vs-systemd-timers-the-friendly-way-to-ship-reliable-schedules-and-real-healthchecks\/\">Cron vs systemd timers and when to choose each<\/a>.<\/p>\n<h3><span id=\"Creating_a_systemd_Unit_for_Laravel_Queues\">Creating a systemd Unit for Laravel Queues<\/span><\/h3>\n<p>Save the following unit under the shown path. The application\/user\/PHP prerequisites are the same as above. After=network.target provides ordering, not proof that Redis is ready or reachable:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">\/etc\/systemd\/system\/laravel-queue-default.service\n<\/code><\/pre>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">[Unit]\nDescription=Laravel Queue Worker (default)\nAfter=network.target\nStartLimitIntervalSec=60\nStartLimitBurst=5\n\n[Service]\nType=simple\nUser=www-data\nGroup=www-data\nWorkingDirectory=\/var\/www\/myapp\nExecStart=\/usr\/bin\/php \/var\/www\/myapp\/artisan queue:work redis --queue=default --sleep=1 --tries=3 --backoff=10 --timeout=60 --max-time=3600\nRestart=always\nRestartSec=5\nKillSignal=SIGTERM\nKillMode=mixed\nTimeoutStopSec=75\nStandardOutput=journal\nStandardError=journal\n\n[Install]\nWantedBy=multi-user.target\n<\/code><\/pre>\n<p>Verify the unit, resolve diagnostics, then load and enable it:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">sudo systemd-analyze verify \/etc\/systemd\/system\/laravel-queue-default.service\nsudo systemctl daemon-reload\nsudo systemctl enable --now laravel-queue-default.service\nsudo systemctl status laravel-queue-default.service\nsudo journalctl -u laravel-queue-default.service -n 100 --no-pager\n<\/code><\/pre>\n<p>Restart=always includes clean recycling exits; an explicit manager stop leaves the service stopped. StartLimitIntervalSec and StartLimitBurst belong in [Unit], and hitting the limit can leave the service down until intervention. KillMode=mixed sends TERM to the main process first; when it exits, remaining children may be killed, or the cgroup can be forcibly killed after TimeoutStopSec. Graceful stopping reduces interruption but does not guarantee exactly-once external effects.<\/p>\n<p>PHP CLI may use a different version, extensions and memory limit from PHP-FPM. Laravel .env syntax is not systemd EnvironmentFile syntax. Environment= passes explicit values; it does not load the Laravel file. With config caching, refresh the intended configuration during deployment and restart workers, since Laravel does not reload .env from cached configuration. For classic workers, queue:restart records a signal in cache, so the command and workers need the same supported, persistent, accessible cache store.<\/p>\n<p>The example uses journal output. Configure journald retention\/persistence and separately retain Laravel application logs. StandardOutput=append: does not rotate a file. Optional CPUQuota=150% limits the unit to 1.5 CPUs of time, not 150% of the whole VPS. MemoryMax is a hard limit that can cause an OOM kill; tune limits from measurements rather than copying a universal value.<\/p>\n<h3><span id=\"Scaling_with_systemd_Templates\">Scaling with systemd Templates<\/span><\/h3>\n<p>A template can run one instance per queue. Copy the same lifecycle settings into \/etc\/systemd\/system\/laravel-queue&#64;.service and use the instance name as the queue name. This replaces the standalone default service for those queues; do not leave redundant consumers running.<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">\/etc\/systemd\/system\/laravel-queue&#64;.service<\/code><\/pre>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">[Unit]\nDescription=Laravel Queue Worker (%i)\nAfter=network.target\nStartLimitIntervalSec=60\nStartLimitBurst=5\n\n[Service]\nType=simple\nUser=www-data\nGroup=www-data\nWorkingDirectory=\/var\/www\/myapp\nExecStart=\/usr\/bin\/php \/var\/www\/myapp\/artisan queue:work redis --queue=%i --sleep=1 --tries=3 --backoff=10 --timeout=60 --max-time=3600\nRestart=always\nRestartSec=5\nKillSignal=SIGTERM\nKillMode=mixed\nTimeoutStopSec=75\nStandardOutput=journal\nStandardError=journal\n\n[Install]\nWantedBy=multi-user.target\n<\/code><\/pre>\n<p>Verify the template instance before activation, then start one worker for each intended queue:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">sudo systemd-analyze verify \/etc\/systemd\/system\/laravel-queue&#64;mail.service\nsudo systemctl daemon-reload\nsudo systemctl enable --now laravel-queue&#64;mail.service\nsudo systemctl enable --now laravel-queue&#64;report.service\nsudo systemctl enable --now laravel-queue&#64;realtime.service\n<\/code><\/pre>\n<p>Here @mail becomes &#8211;queue=mail. @mail-2 would consume a different queue named mail-2; it does not create a second mail consumer automatically. To run two workers on mail, deliberately map two distinct service instances to &#8211;queue=mail, for example with separate unit definitions. Increase long-report timeout, retry_after and stop budgets together; the default 60\/90\/75-second example is not suitable for every report.<\/p>\n<h3><span id=\"Pros_and_Cons_of_systemd\">Pros and Cons of systemd<\/span><\/h3>\n<ul>\n<li><strong>Pros:<\/strong>\n<ul>\n<li>No extra process manager to install<\/li>\n<li>Powerful restart and resource control features<\/li>\n<li>Good integration with OS boot and dependencies<\/li>\n<li>Works equally well for PHP, Node.js, workers, timers, etc.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Cons:<\/strong>\n<ul>\n<li>Unit file syntax and semantics can feel complex at first<\/li>\n<li>Developers less familiar with Linux internals may find troubleshooting harder<\/li>\n<li>Log access via <code>journalctl<\/code> needs a little orientation<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2><span id=\"Option_3_PM2_for_Nodejs_and_Mixed_Stacks\">Option 3: PM2 for Node.js and Mixed Stacks<\/span><\/h2>\n<p>A mixed stack can use Laravel for its API and PHP jobs while Node.js handles another service or consumer. Keep each producer and consumer on a supported message contract. A shared Redis server does not make Laravel Redis queues and Bull\/BullMQ queues interoperable.<\/p>\n<p>PM2 can manage the Node.js processes and their boot registration, logs and restart policy. Its cluster\/reload features target supported application lifecycles; they are not a general guarantee of uninterrupted or duplicate-free queue processing.<\/p>\n<h3><span id=\"PM2_Basics\">PM2 Basics<\/span><\/h3>\n<p>Assume the Node.js application and its queue library are installed under \/var\/www\/myapp-node, and worker.js has the required connection and job logic. Use a restricted application account with an appropriately writable npm global installation path. Save this declaration as ecosystem.config.cjs:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">module.exports = {\n  apps: [{\n    name: &#039;myapp-node-worker&#039;,\n    cwd: &#039;\/var\/www\/myapp-node&#039;,\n    script: &#039;.\/worker.js&#039;,\n    exec_mode: &#039;fork&#039;,\n    instances: 1,\n    autorestart: true,\n    restart_delay: 5000,\n    kill_timeout: 75000,\n    time: true\n  }]\n};\n<\/code><\/pre>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">npm install -g pm2\ncd \/var\/www\/myapp-node\npm2 start ecosystem.config.cjs\npm2 startup systemd\n<\/code><\/pre>\n<p>Review and execute the privileged setup command printed by pm2 startup for the same app user, home directory and Node.js path. Then, as that app user:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">pm2 save\npm2 list\npm2 logs myapp-node-worker --lines 100\n<\/code><\/pre>\n<p>The saved list is restored at boot only when startup registration is correctly installed. pm2 save alone does not install a boot service. Rebuild the startup registration when the Node.js path changes. kill_timeout=75000 is in milliseconds: the worker must handle SIGINT\/SIGTERM, stop taking new work, finish active work and close connections. For BullMQ JavaScript\/TypeScript workers, await worker.close() waits for active jobs without its own timeout. Bound job and I\/O duration so shutdown fits the manager deadline; a forced kill can lead to stalled-job recovery and repeated effects.<\/p>\n<h3><span id=\"PM2_with_Laravel_Nodejs\">PM2 with Laravel + Node.js<\/span><\/h3>\n<p>If your background infrastructure looks like this:<\/p>\n<ul>\n<li>Laravel publishes Laravel jobs for PHP workers using its own queue connection.<\/li>\n<li>A Node.js producer uses its supported queue library, such as BullMQ Queue.add, for its Node.js consumers.<\/li>\n<li>Cross-language work crosses an explicit authenticated API or event bridge with a versioned payload, idempotency key, retry policy and distinct accepted\/completed states. Separate namespaces are necessary but do not translate the protocols.<\/li>\n<\/ul>\n<p>You can comfortably mix approaches:<\/p>\n<ul>\n<li>Use Supervisor or systemd for each assigned PHP worker or Horizon group; do not accidentally add standalone workers to Horizon-owned queues.<\/li>\n<li>Use PM2 or direct systemd for a separate Node.js consumer group. Avoid two managers independently launching the same group.<\/li>\n<li>Node.js is not automatically faster for CPU-heavy work: blocking JavaScript blocks the event loop. Consider worker_threads, a supported sandboxed processor or a separate process, and measure the workload.<\/li>\n<\/ul>\n<p>We go deeper specifically into Node.js production setups (including PM2 vs systemd and Nginx fronting) in our article on <a href=\"https:\/\/www.dchost.com\/blog\/en\/how-i-host-node-js-in-production-without-drama-pm2-systemd-nginx-ssl-and-zero-downtime-deploys\/\">deploying Node.js with PM2, systemd and Nginx<\/a>.<\/p>\n<h3><span id=\"Pros_and_Cons_of_PM2\">Pros and Cons of PM2<\/span><\/h3>\n<ul>\n<li><strong>Pros:<\/strong> Node.js ecosystem declarations, per-process operations, logs and supported cluster management.<\/li>\n<li><strong>Tradeoffs:<\/strong> Another managed daemon, often registered with systemd; a separate log-retention policy; application-specific signal handling. Cluster\/reload can fall back to a restart and does not remove the need for idempotent jobs.<\/li>\n<li><strong>Alternative:<\/strong> Direct systemd may suit teams that want one operating model. PM2 is optional when Node.js is absent.<\/li>\n<\/ul>\n<h2><span id=\"Horizon_and_Advanced_Laravel_Queue_Management\">Horizon and Advanced Laravel Queue Management<\/span><\/h2>\n<p>Horizon adds queue metrics and manages Laravel Redis worker groups. Install and configure it before using the example below. Laravel 13 documentation requires Redis and does not support Redis Cluster with Horizon. The outer Supervisor\/systemd service keeps the Horizon parent alive; Horizon owns its configured workers.<\/p>\n<h3><span id=\"How_Horizon_Fits_into_the_Picture\">How Horizon Fits into the Picture<\/span><\/h3>\n<p>Horizon adds several things:<\/p>\n<ul>\n<li>A dashboard showing queue throughput, failed jobs, processing time, etc.<\/li>\n<li>Named queues and supervisors with different worker counts.<\/li>\n<li>Tags and metrics for jobs.<\/li>\n<\/ul>\n<p>For \/etc\/systemd\/system\/horizon.service, use the same app-user\/PHP prerequisites and a bounded stop policy. Horizon\u2019s own supervisor timeout and job timeouts must fit below the queue retry_after and the outer stop deadline. The shown 75-second stop budget assumes short jobs that fit the earlier 60\/90-second example:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">[Unit]\nDescription=Laravel Horizon\nAfter=network.target\nStartLimitIntervalSec=60\nStartLimitBurst=5\n\n[Service]\nType=simple\nUser=www-data\nGroup=www-data\nWorkingDirectory=\/var\/www\/myapp\nExecStart=\/usr\/bin\/php \/var\/www\/myapp\/artisan horizon\nRestart=always\nRestartSec=5\nKillSignal=SIGTERM\nKillMode=mixed\nTimeoutStopSec=75\nStandardOutput=journal\nStandardError=journal\n\n[Install]\nWantedBy=multi-user.target\n<\/code><\/pre>\n<p>Verify the unit before enabling it, then check the journal and a representative job. Configure the relevant environment and queue names in config\/horizon.php. When using automatic balancing, keep Horizon\u2019s supervisor timeout greater than any job-specific timeout, and several seconds below retry_after. Deployments use horizon:terminate so the manager can start a fresh Horizon process after current work completes. Restrict the Horizon dashboard with its authorization gate; production access must be intentional.<\/p>\n<p>A possible layout is:<\/p>\n<ul>\n<li>One horizon.service owns its explicitly assigned Laravel Redis queues.<\/li>\n<li>Standalone workers may consume other deliberately disjoint queues with a compatible application\/runtime. Do not mix PHP versions and serialized job classes without checking application compatibility.<\/li>\n<\/ul>\n<p>For more Laravel-specific tuning (FPM pools, OPcache, Octane, Redis, Horizon, etc.), see the checklist in <a href=\"https:\/\/www.dchost.com\/blog\/en\/laravel-production-optimization\/\">our Laravel production optimization guide for VPS servers<\/a>.<\/p>\n<h2><span id=\"Monitoring_Scaling_and_Troubleshooting_Queues_on_a_VPS\">Monitoring, Scaling and Troubleshooting Queues on a VPS<\/span><\/h2>\n<p>Setting up workers is only half the story. The other half is making sure they\u2019re actually running, not stuck, and sized correctly for your VPS resources.<\/p>\n<h3><span id=\"Monitoring_Worker_Health\">Monitoring Worker Health<\/span><\/h3>\n<p>Choose a monitoring layer that reports both process state and completed work. At minimum, check:<\/p>\n<ul>\n<li>Are the intended workers running, restarting repeatedly or stopped by start limits?<\/li>\n<li>What are queue length, oldest-job age and enqueue\/completion rates?<\/li>\n<li>Are failures, retries, timeouts or stalled jobs increasing?<\/li>\n<li>Are CPU, RAM, disk I\/O, Redis\/DB connections or external API limits constraining progress?<\/li>\n<\/ul>\n<p>There are three layers you can combine:<\/p>\n<ol>\n<li><strong>OS-level checks:<\/strong> Use <code>systemctl status<\/code>, <code>supervisorctl status<\/code>, or <code>pm2 list<\/code> for process diagnostics; automate structured status collection and restart\/start-limit alerts. RUNNING does not prove queue health.<\/li>\n<li><strong>Application-level checks:<\/strong> Use Horizon or application metrics for queue lengths, oldest-job age, completed work and failures. Restrict operational endpoints and dashboards.<\/li>\n<li><strong>Full monitoring stack:<\/strong> Tools like Prometheus + Grafana + Uptime Kuma as described in our guide on <a href=\"https:\/\/www.dchost.com\/blog\/en\/vps-monitoring-prometheus-grafana-uptime-kuma\/\">setting up VPS monitoring and alerts<\/a>.<\/li>\n<\/ol>\n<h3><span id=\"Capacity_Planning_for_Workers\">Capacity Planning for Workers<\/span><\/h3>\n<p>Workers share the same CPU and RAM as your web stack, database (if on the same server), and cache. On a small VPS, it\u2019s easy to overshoot with too many workers and starve everything else.<\/p>\n<p>Some practical rules of thumb:<\/p>\n<ul>\n<li>On a small VPS hosting Laravel, MySQL and Redis together, begin with low worker concurrency and measure a representative workload. Budget per-job memory, CPU time, I\/O, database connections and API quotas while reserving web capacity. A 2 vCPU \/ 4 GB label does not determine a safe worker count.<\/li>\n<li>Separate heavy reports from latency-sensitive work. Separate queues still share host and backend resources; adjust long-job timeouts and stop budgets together.<\/li>\n<li>A worker can prioritize queues with &#8211;queue=high,default,low, but sustained high-priority traffic may starve the others. Use dedicated worker groups where latency goals require capacity reservations.<\/li>\n<li>Move CPU-heavy processing to additional capacity when measurement justifies it; plan broker access, credentials, data and failure behavior before adding a VPS. No single average-CPU percentage proves the system is healthy.<\/li>\n<\/ul>\n<h3><span id=\"Common_Queue_Problems_and_How_to_Fix_Them\">Common Queue Problems and How to Fix Them<\/span><\/h3>\n<ul>\n<li><strong>Reserved\/stuck jobs:<\/strong> Check whether an execution is still active, the reservation\/visibility deadline, errors and consumer connectivity. &#8211;max-time and &#8211;max-jobs recycle between jobs and will not interrupt a stuck call; use job\/I\/O timeouts with timeout &lt; retry_after.<\/li>\n<li><strong>Database queue contention:<\/strong> Inspect query\/lock load, retention and worker concurrency. Redis can be an alternative when measurements and recovery needs justify it; a queue count alone is not a migration threshold. Drain the old backend or keep its consumers until pending work is handled.<\/li>\n<li><strong>Memory failures:<\/strong> Investigate job size and memory use. Recycling releases process memory between jobs but cannot make a single oversized job fit; hard memory limits can kill it mid-work.<\/li>\n<li><strong>Duplicate effects during deployment:<\/strong> Refresh configuration and use queue:restart or horizon:terminate for the appropriate owner. Allow bounded graceful stopping, and use idempotency or reconciliation for external effects. Correct timing reduces overlap but does not create exactly-once delivery.<\/li>\n<\/ul>\n<h2><span id=\"Choosing_Supervisor_systemd_or_PM2_for_Your_VPS\">Choosing Supervisor, systemd or PM2 for Your VPS<\/span><\/h2>\n<p>Choose from the workload\u2019s runtime, the team\u2019s operating model and required resource\/log controls. These scenarios are starting points, not observed customer outcomes:<\/p>\n<h3><span id=\"Scenario_1_Pure_Laravel_App_Small_to_Medium_Scale\">Scenario 1: Pure Laravel App, Small to Medium Scale<\/span><\/h3>\n<p>For a classic Laravel application (API or web) on a single VPS:<\/p>\n<ul>\n<li><strong>Backend:<\/strong> Database or Redis according to measured throughput, dependencies and recovery requirements. Database is not limited to an arbitrary traffic category; Redis needs memory, persistence and eviction planning.<\/li>\n<li><strong>Manager:<\/strong> Supervisor can suit a readable group-based workflow; systemd can suit native service, journal and resource controls. Either can be used at different scales, with optional Horizon on supported Redis queues.<\/li>\n<li><strong>Monitoring:<\/strong> Track process state alongside oldest-job age, throughput and failure alerts. Test stop\/restart and representative job outcomes before relying on the setup.<\/li>\n<\/ul>\n<h3><span id=\"Scenario_2_Laravel_Nodejs_Mixed_Stack\">Scenario 2: Laravel + Node.js Mixed Stack<\/span><\/h3>\n<p>For stacks with both Laravel and Node.js:<\/p>\n<ul>\n<li>Run Laravel PHP worker groups under Supervisor or systemd, with explicit ownership.<\/li>\n<li>Run separate Node.js groups under PM2 or direct systemd.<\/li>\n<li>Use compatible producers and consumers with an explicit cross-language bridge where needed. Laravel\u2019s serialized PHP payload is not BullMQ\u2019s format, even on the same Redis server.<\/li>\n<li>Use distinct queue\/key namespaces and account for shared memory, eviction and failure domains; namespace separation is not resource isolation.<\/li>\n<\/ul>\n<h3><span id=\"Scenario_3_Multiple_Apps_on_One_VPS\">Scenario 3: Multiple Apps on One VPS<\/span><\/h3>\n<p>If you host multiple Laravel apps on a single dchost.com VPS, organization becomes more important:<\/p>\n<ul>\n<li>Use clear process\/unit names such as project1-queue and project2-horizon.<\/li>\n<li>Use restricted per-project users and appropriate permissions for code, runtime files and secrets.<\/li>\n<li>Give Supervisor workers distinct rotating files; use unit-scoped journal records for systemd and a retention policy for PM2 and app logs.<\/li>\n<li>Keep separate process ownership and queue namespaces; aggregate CPU\/RAM, storage and connection budgets before moving busy projects to extra capacity.<\/li>\n<\/ul>\n<h3><span id=\"Security_and_Stability_Considerations\">Security and Stability Considerations<\/span><\/h3>\n<p>Background jobs are powerful \u2013 they often talk to payment gateways, third-party APIs, external storage and internal systems. Make sure they run on a hardened VPS with:<\/p>\n<ul>\n<li>Restricted worker accounts and appropriate application\/file\/secret permissions; do not run application workers as root.<\/li>\n<li>Controlled SSH access and firewall rules, including restricted Redis access with appropriate authentication\/TLS for the network.<\/li>\n<li>Regular software\/kernel updates and tested, versioned off-site backups with an explicit acceptable-loss target.<\/li>\n<li>Validated job payloads, limited outbound credentials and idempotency for sensitive external effects.<\/li>\n<\/ul>\n<p>We\u2019ve summarized a practical, non-dramatic hardening checklist for new servers in our article on <a href=\"https:\/\/www.dchost.com\/blog\/en\/vps-security-checklist\/\">how to secure a VPS server the calm way<\/a>. Apply it together with tested queue recovery, access controls and workload monitoring.<\/p>\n<h2><span id=\"Putting_It_All_Together_on_Your_dchostcom_VPS\">Putting It All Together on Your dchost.com VPS<\/span><\/h2>\n<p>A dependable Laravel queue setup combines suitable job design, a supported backend, bounded retry\/timeouts, a single owner for each worker group and outcome monitoring. Queueing can keep suitable work out of HTTP requests; it does not reduce total work or automatically ensure delivery. Supervisor and systemd manage PHP workers, while PM2 is one option for a separate Node.js service.<\/p>\n<p>Choose database or Redis from requirements, then validate a small worker set. Add Horizon where its Redis compatibility and metrics fit; add Node.js consumers only behind a defined protocol. Follow the <a href=\"https:\/\/www.dchost.com\/blog\/en\/vps-monitoring-prometheus-grafana-uptime-kuma\/\">VPS monitoring and alerts guide<\/a> for the surrounding monitoring stack. If you add a VPS or <a href=\"https:\/\/www.dchost.com\/dedicated-server\">dedicated server<\/a>, plan network access, credentials, shared data and backend capacity before moving work.<\/p>\n<p>For a new application or migration, use representative jobs to validate queue delay, failures, retries, graceful stopping and resource use. Match the VPS size and storage to those measurements. Keep a deployment and recovery procedure that distinguishes accepted work from completed external actions, and document which operations can safely be replayed.<\/p>\n<h2><span id=\"Primary_Technical_References\">Primary Technical References<\/span><\/h2>\n<ul>\n<li><a href=\"https:\/\/laravel.com\/framework\/docs\/13.x\/queues\" rel=\"nofollow noopener\" target=\"_blank\">Laravel 13 \u2014 Queues<\/a><\/li>\n<li><a href=\"https:\/\/laravel.com\/framework\/docs\/13.x\/horizon\" rel=\"nofollow noopener\" target=\"_blank\">Laravel 13 \u2014 Horizon<\/a><\/li>\n<li><a href=\"https:\/\/laravel.com\/framework\/docs\/13.x\/configuration\" rel=\"nofollow noopener\" target=\"_blank\">Laravel 13 \u2014 Configuration<\/a><\/li>\n<li><a href=\"https:\/\/supervisord.org\/configuration.html\" rel=\"nofollow noopener\" target=\"_blank\">Supervisor \u2014 Configuration<\/a><\/li>\n<li><a href=\"https:\/\/www.freedesktop.org\/software\/systemd\/man\/252\/systemd.service.html\" rel=\"nofollow noopener\" target=\"_blank\">systemd 252 \u2014 Services<\/a><\/li>\n<li><a href=\"https:\/\/pm2.keymetrics.io\/docs\/usage\/startup\/\" rel=\"nofollow noopener\" target=\"_blank\">PM2 \u2014 Startup<\/a><\/li>\n<li><a href=\"https:\/\/docs.bullmq.io\/guide\/workers\/graceful-shutdown\" rel=\"nofollow noopener\" target=\"_blank\">BullMQ \u2014 Graceful shutdown<\/a><\/li>\n<li><a href=\"https:\/\/nodejs.org\/docs\/latest-v22.x\/api\/worker_threads.html\" rel=\"nofollow noopener\" target=\"_blank\">Node.js \u2014 Worker threads<\/a><\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Run Laravel 13 queue workers with Supervisor or systemd, and separate Node.js consumers with PM2. Align retries, timeouts, graceful shutdown, logging and capacity planning.<\/p>\n","protected":false},"author":1,"featured_media":12689,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[184],"tags":[],"class_list":["post-3505","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-technology"],"_links":{"self":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/3505","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/comments?post=3505"}],"version-history":[{"count":6,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/3505\/revisions"}],"predecessor-version":[{"id":12691,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/3505\/revisions\/12691"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/media\/12689"}],"wp:attachment":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/media?parent=3505"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/categories?post=3505"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/tags?post=3505"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}