'How do I setup and use Laravel Scheduling on AWS Elastic Beanstalk?
Scenario
As a fairly new user of Laravel and Elastic Beanstalk I soon found my self in the need to schedule operations, like most of us do.
In the past I had always used simple crontab scheduling for this. So now I stood before a list of questions:
- How do I run Laravel code using crontab?
- How do I setup crontab in my Elastic Beanstalk environment?
Finding the individual answers to these questions weren't that hard. Combining them and actually getting it all to work however turned out to be a bit tricky, which is why I've decided to share the solution here for others struggling with getting this to work properly.
Environment
- Laravel 5.6
- PHP 7.1
Solution 1:[1]
A simpler approach is to use the new Periodic Tasks feature. Using the .ebextensions for cron jobs may lead to multiple machines running the same job or other race conditions with auto-scaling.
Jobs defined in cron.yaml are loaded only by the Worker environment and are guaranteed to run only by one machine at a time (the leader). It has a nice syncing mechanism to make sure there's no duplication. From the docs:
Elastic Beanstalk uses leader election to determine which instance in your worker environment queues the periodic task. Each instance attempts to become leader by writing to an Amazon DynamoDB table. The first instance that succeeds is the leader, and must continue to write to the table to maintain leader status. If the leader goes out of service, another instance quickly takes its place.
Creating a Cron for a Single or Multiple Workers
Place cron.yaml in the root of the project:
version: 1
cron:
- name: "schedule"
url: "/worker/schedule"
schedule: "* * * * *"
One thing to take into consideration is that in Beanstalk periodic tasks are designed to make an HTTP POST request to a URL in your application that in turn triggers the job you want to run. This is similar to how it also manages queues with SQS.
For Laravel
For Laravel specifically, you may create the routes and controllers to handle each scheduled job. But a better approach is to use Laravel's scheduler and have a single route that you call every minute.
This package will create those routes automatically for you https://github.com/dusterio/laravel-aws-worker
Troubleshooting Permissions
If you are running into trouble with the DynamoDB create Leader Table permissions when triggering a deploy from CodePipeline, it's because the CodePileline service role needs dynamodb:CreateTable. For instructions check these StackOverflow Question
Solution 2:[2]
you can use this with amazon Linux 2 using .ebextensions configurations you can run the commands directly
first you need to configure the command in separate file create file under .ebextensions called cron_job.txt and add this line
* * * * * root . /opt/elasticbeanstalk/deployment/env && /usr/bin/php /var/www/html/artisan schedule:run 1>> /var/www/html/laralog.log 2>&1
notice that the first part different from amazon Linux 2 from amazon Linux 1
it loads the environment variables:
Linux 1 : . /opt/elasticbeanstalk/support/envvars
Linux 2 : . /opt/elasticbeanstalk/deployment/env
and after initialing the command in this separated file
we need to fire it through the init.config file which have the container commands in .ebextensions
we can define it as follow :
container_commands:
03cronjob:
command: 'cat .ebextensions/cron_jobs.txt > /etc/cron.d/cron_jobs && chmod 644 /etc/cron.d/cron_jobs'
and that's it you can try it and find the cron jobs executed successfully.
and you can also read this explained article https://medium.com/qosoor/the-ultimate-guide-to-setup-cron-jobs-with-laravel-elastic-beanstalk-d497daaca1b0
Hope this is helpful
Solution 3:[3]
In AWS ECS we can use this without adding cron in to the container
https://github.com/spatie/laravel-cronless-schedule
This is how you can start the cronless schedule:
php artisan schedule:run-cronless
Solution 4:[4]
This is for docker users, had some trouble with this so thought its worth posting.
The cron needs to be added to schedule_run file on the server. However, even if you add a container_name to the Dockerrun.aws.json file it changes it to that plus some extra information and therefore you cannot use the normal service name to run the cron.
So using $(docker ps -qf name=php-fpm) where name is part of the name of your container it will return the ID of the container. My container is called php-fpm.
Here is my working file (.ebextensions/01-cron.config).
files:
"/etc/cron.d/schedule_run":
mode: "000644"
owner: root
group: root
content: |
* * * * * root docker exec -t $(docker ps -qf name=php-fpm) sh -c "php artisan schedule:run" >> /var/log/eb-cron.log 2>&1
commands:
002-remove_old_cron:
command: "rm -f /etc/cron.d/*.bak"
Note: it mite be that the first time this cron runs the container is not up. Since the cron in my example is running each minute it didnt matter too much as by the time it runs the 2nd time, the container is up and working.
Solution 5:[5]
After several efforts, I found an alternative way to run a cron job easily. You can run a cron job easily in 3 Steps.
Step 1: Create a route
routes/web.php
Route::get('/cron/run',[HomeController::class, 'cron'])->name('cron');
Step 2: Create a function in the HomeController
public function cron()
{
\Artisan::call("schedule:run");
return 'run cron successful';
}
Step 3:
Run the URL every minute using https://cron-job.org/en/
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Community |
| Solution 2 | Alaa Moneam |
| Solution 3 | Bira |
| Solution 4 | Oli Girling |
| Solution 5 | Harshith VA |
