├── .platform ├── hooks │ ├── prebuild │ │ ├── update_composer.sh │ │ ├── configure_php_ini.sh │ │ ├── install_latest_chromium_binary.sh │ │ ├── install_imagick.sh │ │ ├── install_latest_node_js.sh │ │ ├── install_phpredis.sh │ │ ├── install_supervisor.sh │ │ └── install_memcached_discovery.sh │ ├── postdeploy │ │ ├── run_caches.sh │ │ ├── restart_supervisorctl.sh │ │ ├── x_make_folders_writable.sh │ │ ├── restart_services.sh │ │ ├── create_cron_files.sh │ │ └── x_optimize_php.sh │ └── predeploy │ │ └── restart_services.sh ├── nginx │ └── conf.d │ │ └── elasticbeanstalk │ │ ├── https.conf │ │ ├── static.conf │ │ └── laravel.conf ├── files │ ├── supervisor.ini │ └── php.ini └── scripts │ └── spot-termination.sh ├── .editorconfig ├── .ebextensions ├── 00_pass_envs_to_server.config ├── 01_deploy.config ├── 00_copy_env_file.config ├── 00_custom.config ├── 02_logs_cloudwatch_imds2.config └── 01_logs_streamtocloudwatch.config └── README.md /.platform/hooks/prebuild/update_composer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Update Composer binary. 4 | 5 | export COMPOSER_HOME=/root 6 | 7 | sudo COMPOSER_MEMORY_LIMIT=-1 /usr/bin/composer.phar self-update 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.platform/hooks/postdeploy/run_caches.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run Caches 4 | 5 | # After the deployment, it's highly recommended 6 | # to re-run the caches for config, routes and views. 7 | 8 | php artisan config:cache 9 | 10 | php artisan route:cache 11 | 12 | php artisan view:cache 13 | -------------------------------------------------------------------------------- /.platform/hooks/prebuild/configure_php_ini.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Add custom configuration to PHP 4 | 5 | # The .platform/files/php.ini files contains enabled OP-cache 6 | # configuration. Feel free to edit it in case you want custom configuration. 7 | 8 | sudo cp .platform/files/php.ini /etc/php.d/laravel.ini 9 | -------------------------------------------------------------------------------- /.platform/nginx/conf.d/elasticbeanstalk/https.conf: -------------------------------------------------------------------------------- 1 | # HTTP to HTTPS 2 | 3 | # Uncomment the following lines of code 4 | # to enable the force HTTP to HTTPS rule 5 | # for the NGINX Proxy service. 6 | 7 | # if ($http_x_forwarded_proto = 'http') { 8 | # return 301 https://$host$request_uri; 9 | # } 10 | -------------------------------------------------------------------------------- /.platform/hooks/postdeploy/restart_supervisorctl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Restart Supervisor workers 4 | 5 | # During deployment, some of the workers might have 6 | # SPAWN ERR errors, so it's better to restart them. 7 | 8 | # At this point in time, the whole app 9 | # has already been deployed. 10 | 11 | sudo supervisorctl restart all 12 | -------------------------------------------------------------------------------- /.platform/hooks/prebuild/install_latest_chromium_binary.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Install the Chrome Binary release to support Chrome-based API. 4 | # The binary will be available at this path: /usr/bin/google-chrome-stable 5 | 6 | # curl https://gist.githubusercontent.com/rennokki/813867e8d5572d0ea51b1551dc27cd2a/raw/e368c5a6a12e22739247a97d9bf21ca399224da9/install_latest_chrome_binary.sh | sudo bash 7 | -------------------------------------------------------------------------------- /.platform/hooks/postdeploy/x_make_folders_writable.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Make Folders Writable 4 | 5 | # After the deployment finished, give the full 0777 permissions 6 | # to some folders that should be writable, such as the storage/ 7 | # or bootstrap/cache/, for example. 8 | 9 | sudo chmod -R 777 storage/ 10 | sudo chmod -R 777 bootstrap/cache/ 11 | 12 | # Storage Symlink Creation 13 | 14 | php artisan storage:link 15 | -------------------------------------------------------------------------------- /.platform/hooks/prebuild/install_imagick.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Some packages like spatie/laravel-medialibrary needs Imagick installed. 4 | # Uncomment the following lines to install Imagick on each deploy. 5 | 6 | # set +e 7 | 8 | # sudo amazon-linux-extras enable epel 9 | 10 | # sudo yum clean metadata 11 | 12 | # sudo yum install -y epel-release 13 | 14 | # sudo yum install -y ImageMagick ImageMagick-devel 15 | 16 | # printf '\n' | : sudo pecl install imagick 17 | -------------------------------------------------------------------------------- /.platform/hooks/postdeploy/restart_services.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This file will make sure to restart on specific services like NGINX. 4 | # During postdeploy, we make sure to restart the services to take in consideration the new custom configs. 5 | # Please see the related issue(s): https://github.com/rennokki/laravel-aws-eb/issues/55 6 | 7 | # Feel free to uncomment the following lines in case you have issues. ¯\_(ツ)_/¯ 8 | 9 | # sudo systemctl restart nginx.service 10 | -------------------------------------------------------------------------------- /.platform/hooks/predeploy/restart_services.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This file will run restarts on specific services like PHP-FPM or NGINX. 4 | # During prebuild, some PHP extensions might be installed and a restart 5 | # might be needed. 6 | 7 | # Out-of-the box, the extension installs are already loaded, so no need to do that. 8 | # However, feel free to uncomment the following in case you have trust issues. ¯\_(ツ)_/¯ 9 | 10 | # sudo systemctl restart php-fpm.service 11 | 12 | # sudo systemctl restart nginx.service 13 | -------------------------------------------------------------------------------- /.ebextensions/00_pass_envs_to_server.config: -------------------------------------------------------------------------------- 1 | # Here you can pass references from the stack into the $_SERVER variable. This is useful 2 | # when you need to know within your code certain parameters handled by AWS, like the Region name. 3 | # Uncomment to enable. Check docs here, under "Software setting namespaces": https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-cfg-softwaresettings.html 4 | 5 | #option_settings: 6 | # aws:elasticbeanstalk:application:environment: 7 | # AWS_REGION: '`{"Ref" : "AWS::Region"}`' 8 | -------------------------------------------------------------------------------- /.platform/hooks/prebuild/install_latest_node_js.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Install Latest Node 4 | 5 | # Some Laravel apps need Node & NPM for the frontend assets. 6 | # This script installs the latest Node 12.x alongside 7 | # with the paired NPM release. 8 | 9 | sudo yum remove -y nodejs npm 10 | 11 | sudo rm -fr /var/cache/yum/* 12 | 13 | sudo yum clean all 14 | 15 | curl --silent --location https://rpm.nodesource.com/setup_12.x | sudo bash - 16 | 17 | sudo yum install nodejs -y 18 | 19 | # Uncomment this line and edit the Version of NPM 20 | # you want to install instead of the default one. 21 | # npm i -g npm@6.14.4 22 | -------------------------------------------------------------------------------- /.platform/hooks/prebuild/install_phpredis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Install PhpRedis 4 | 5 | # Laravel uses by default PhpRedis, so the extension needs to be installed. 6 | # https://github.com/phpredis/phpredis 7 | 8 | # For Predis, this extension is not necessarily needed. 9 | # Enabled by default since it's latest Laravel version's default driver. 10 | 11 | sudo yum -y install php-redis 12 | 13 | # For PHP 8.0+, you can use the pecl command to install it. You may disable the previous command. 14 | 15 | # pecl install redis 16 | # sed -i -e '/extension="redis.so"/d' /etc/php.ini 17 | # echo 'extension="redis.so"' > /etc/php.d/41-redis.ini 18 | -------------------------------------------------------------------------------- /.platform/hooks/postdeploy/create_cron_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Create CRON files 4 | 5 | # After the deployment finishes, set up a Crontab for 6 | # the php artisan schedule:run command 7 | 8 | echo "* * * * * root /usr/bin/php /var/app/current/artisan schedule:run 1>> /dev/null 2>&1" \ 9 | | sudo tee /etc/cron.d/artisan_scheduler 10 | 11 | # In some cases, Laravel logs a lot of data in the storage/logs/laravel.log and it sometimes 12 | # might turn out into massive files that will restrict the filesystem. 13 | # Uncomment the following lines to enable a CRON that deletes the laravel.log file 14 | # every now and often. 15 | 16 | # echo "0 0 * * */7 root rm -rf /var/app/current/storage/logs/laravel.log 1>> /dev/null 2>&1" \ 17 | # | sudo tee /etc/cron.d/log_deleter 18 | -------------------------------------------------------------------------------- /.platform/nginx/conf.d/elasticbeanstalk/static.conf: -------------------------------------------------------------------------------- 1 | # NGINX can tell the browser to stop re-requesting 2 | # files from certain paths if it cached them in the memory. 3 | # It is a lot faster, and it will stop putting pressure on the server. 4 | 5 | # By default, it is deactivated, but feel free to edit it 6 | # according to your needs. 7 | # Please see this for more information: 8 | # https://serversforhackers.com/c/nginx-caching 9 | 10 | # location ~* \.(?:rss|atom|json)$ { 11 | # expires 1h; 12 | # add_header Cache-Control "public"; 13 | # } 14 | 15 | # location ~* \.(?:jpg|jpeg|gif|png|ico)$ { 16 | # expires 7d; 17 | # access_log off; 18 | # add_header Cache-Control "public"; 19 | # } 20 | 21 | # location ~* \.(?:css|js)$ { 22 | # expires 7d; 23 | # access_log off; 24 | # add_header Cache-Control "public"; 25 | # } 26 | -------------------------------------------------------------------------------- /.platform/hooks/prebuild/install_supervisor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Install Supervisor using the EPEL repository. 4 | 5 | # This script will install Supervisor and configure your workers 6 | # that are added in the .platform/files/supervisor.ini file. 7 | # It's highly recommended to add all your workers in supervisor.ini 8 | # to avoid conflicts for further updates. 9 | 10 | # The `supervisorctl restart all` command will be 11 | # triggered in the postdeploy hook to avoid 12 | # any errors (the app is not set yet and 13 | # it might trigger spawn errors, which will exit with non-zero code) 14 | 15 | sudo amazon-linux-extras enable epel 16 | 17 | sudo yum install -y epel-release 18 | 19 | sudo yum -y update 20 | 21 | sudo yum -y install supervisor 22 | 23 | sudo systemctl start supervisord 24 | 25 | sudo systemctl enable supervisord 26 | 27 | sudo cp .platform/files/supervisor.ini /etc/supervisord.d/laravel.ini 28 | 29 | sudo supervisorctl reread 30 | 31 | sudo supervisorctl update 32 | -------------------------------------------------------------------------------- /.platform/files/supervisor.ini: -------------------------------------------------------------------------------- 1 | # The following given example is used with Horizon, but you can 2 | # make a copy of it or add your own and configure it. 3 | 4 | # [program:laravel-worker] 5 | # process_name=%(program_name)s_%(process_num)02d 6 | # command=php artisan horizon 7 | # directory=/var/app/current 8 | # autostart=true 9 | # autorestart=true 10 | # killasgroup=true 11 | # stopasgroup=true 12 | # user=root 13 | # numprocs=1 14 | # redirect_stderr=true 15 | # stdout_logfile=/var/log/supervisor-laravel-worker.log 16 | # stopsignal=INT 17 | # stopwaitsecs=60 18 | 19 | # Uncomment the following process to enable 20 | # EC2 spot termination notices. 21 | 22 | # [program:ec2-spot-termination-notice] 23 | # process_name=%(program_name)s_%(process_num)02d 24 | # command=sh spot-termination.sh 25 | # directory=/var/app/current/.platform/scripts 26 | # autostart=true 27 | # autorestart=true 28 | # killasgroup=true 29 | # stopasgroup=true 30 | # user=root 31 | # numprocs=1 32 | # redirect_stderr=true 33 | # stdout_logfile=/var/log/supervisor-ec2-interruption-notice.log 34 | # stopsignal=INT 35 | # stopwaitsecs=60 36 | -------------------------------------------------------------------------------- /.ebextensions/01_deploy.config: -------------------------------------------------------------------------------- 1 | option_settings: 2 | - namespace: aws:elasticbeanstalk:application:environment 3 | option_name: COMPOSER_HOME 4 | value: /root 5 | 6 | # Point the app root to the public/ folder. 7 | - namespace: aws:elasticbeanstalk:container:php:phpini 8 | option_name: document_root 9 | value: /public 10 | 11 | # Set here your php.ini `memory_limit` value. 12 | - namespace: aws:elasticbeanstalk:container:php:phpini 13 | option_name: memory_limit 14 | value: 256M 15 | 16 | container_commands: 17 | 00_copy_env_file: 18 | command: "mv /tmp/.env /var/app/staging/.env" 19 | 20 | 01_install_composer_dependencies: 21 | command: "sudo php -d memory_limit=-1 /usr/bin/composer.phar install --no-dev --no-interaction --prefer-dist --optimize-autoloader" 22 | cwd: "/var/app/staging" 23 | 24 | 02_install_node_dependencies: 25 | command: "sudo npm install" 26 | cwd: "/var/app/staging" 27 | 28 | 03_build_node_assets: 29 | command: "sudo npm run prod" 30 | cwd: "/var/app/staging" 31 | 32 | 04_run_migrations: 33 | command: "php artisan migrate --force" 34 | cwd: "/var/app/staging" 35 | leader_only: true 36 | -------------------------------------------------------------------------------- /.ebextensions/00_copy_env_file.config: -------------------------------------------------------------------------------- 1 | # Replace `elasticbeanstalk-eu-central-1-xxxxxxxxxxxx` with the bucket that AWS created 2 | # when you created your first Elastic Beanstalk environment. 3 | 4 | # Make sure that the IAM Role for the EC2 Instance set in the Elastic Beanstalk configuration 5 | # has attached the `AdministratorAccess-AWSElasticBeanstalk` policy. 6 | 7 | Resources: 8 | AWSEBAutoScalingGroup: 9 | Metadata: 10 | AWS::CloudFormation::Authentication: 11 | S3Auth: 12 | type: "s3" 13 | buckets: ["elasticbeanstalk-eu-central-1-xxxxxxxxxxxx"] 14 | roleName: 15 | "Fn::GetOptionSetting": 16 | Namespace: "aws:autoscaling:launchconfiguration" 17 | OptionName: "IamInstanceProfile" 18 | DefaultValue: "aws-elasticbeanstalk-ec2-role" 19 | 20 | # From the created bucket, point to the .env file which you want to 21 | # copy to this app during deployment. 22 | 23 | # The file will be copied first in /tmp/.env, then moved to the app 24 | # in the deployment process. 25 | 26 | files: 27 | "/tmp/.env": 28 | mode: "000777" 29 | owner: root 30 | group: root 31 | authentication: "S3Auth" 32 | source: https://elasticbeanstalk-eu-central-1-xxxxxxxxxxxx.s3.eu-central-1.amazonaws.com/.env 33 | -------------------------------------------------------------------------------- /.platform/scripts/spot-termination.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Spot Termination notice handler. 4 | # See https://aws.amazon.com/blogs/compute/best-practices-for-handling-ec2-spot-instance-interruptions/ 5 | 6 | # If you are using spot instances, you might want to handle termination notices. 7 | # This script will continuously poll the EC2 Metadata Endpoint every 5 seconds, 8 | # and if it detects a termination notice, it can run your arbitrary code, like safely 9 | # shutting down processes, for example. 10 | 11 | # To active this script, you shall uncomment the .platform/files/supervisor.ini process 12 | # that will keep this script alive throughout the instance lifecycle until its termination. 13 | 14 | TOKEN=`curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` 15 | 16 | while sleep 5; do 17 | 18 | HTTP_CODE=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s -w %{http_code} -o /dev/null http://169.254.169.254/latest/meta-data/spot/instance-action) 19 | 20 | if [[ "$HTTP_CODE" -eq 401 ]] ; then 21 | 22 | # Refreshing the authentication token... 23 | TOKEN=`curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 30"` 24 | 25 | elif [[ "$HTTP_CODE" -eq 200 ]] ; then 26 | 27 | # Here, the instance knows it will be interrupted. 28 | # Run your code. 29 | 30 | fi 31 | 32 | done 33 | -------------------------------------------------------------------------------- /.ebextensions/00_custom.config: -------------------------------------------------------------------------------- 1 | # Here you can define custom parameters for the created AWS resources. 2 | # Each resource will contain details from the docs. 3 | 4 | # For more details regarding other resources, 5 | # check the following link: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-format-resources-eb.html 6 | 7 | Resources: 8 | # Available Properties: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-as-group.html 9 | # [Property] Capacity Rebalacing: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-as-group.html 10 | AWSEBAutoScalingGroup: 11 | Type: "AWS::AutoScaling::AutoScalingGroup" 12 | Properties: 13 | Tags: [{ "Key": "Laravel", "PropagateAtLaunch": True, "Value": "Yes" }] 14 | # CapacityRebalance: True 15 | 16 | # Classic Load Balancer: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-elb.html 17 | # AWSEBLoadBalancer: 18 | # Type: "AWS::ElasticLoadBalancing::LoadBalancer" 19 | # Properties: 20 | # Tags: [{ "Key": "Laravel", "Value": "Yes" }] 21 | 22 | # Application Load Balancer: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-loadbalancer.html 23 | # AWSEBV2LoadBalancer: 24 | # Type: "AWS::ElasticLoadBalancingV2::LoadBalancer" 25 | # Properties: 26 | # Tags: [{ "Key": "Laravel", "Value": "Yes" }] 27 | -------------------------------------------------------------------------------- /.platform/nginx/conf.d/elasticbeanstalk/laravel.conf: -------------------------------------------------------------------------------- 1 | # The following code will sit in the server { } block 2 | # for the NGINX configuration. 3 | # The most important part here is the "location /" block 4 | # to tell that any subsequent route will lead to the index.php 5 | # from the public/ folder. 6 | 7 | # DO NOT set any root here because the root 8 | # is automatically handled by Amazon. 9 | 10 | add_header X-Frame-Options "SAMEORIGIN"; 11 | add_header X-XSS-Protection "1; mode=block"; 12 | add_header X-Content-Type-Options "nosniff"; 13 | 14 | # Sets the maximum allowed size of the client request body. 15 | # If the size in a request exceeds the configured value, 16 | # the 413 (Request Entity Too Large) error is returned to the client 17 | # http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size 18 | 19 | # client_max_body_size 32M; 20 | 21 | # Sets buffer size for reading client request body. 22 | # http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size 23 | 24 | # client_body_buffer_size 16k; 25 | 26 | # Feel free to change the fastcgi buffers in case you have issues with them. 27 | # They should be increased in case your payload (HTTP responses) are big. 28 | 29 | # fastcgi_buffers 16 16k; 30 | # fastcgi_buffer_size 32k; 31 | 32 | charset utf-8; 33 | 34 | location / { 35 | try_files $uri $uri/ /index.php?$query_string; 36 | } 37 | 38 | location = /favicon.ico { access_log off; log_not_found off; } 39 | location = /robots.txt { access_log off; log_not_found off; } 40 | -------------------------------------------------------------------------------- /.platform/hooks/prebuild/install_memcached_discovery.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Memcached Auto Discovery is a Memcached extension that replace the default Memcached 4 | # extension, so you can use the Auto Discovery feature for AWS Memcached clusters. 5 | # Just uncomment the following lines and MAKE SURE YOU RESTART PHP-FPM AND NGINX (please see 6 | # the `restart_services.sh` file and enable the commands from there). 7 | # See more about it here: https://docs.aws.amazon.com/AmazonElastiCache/latest/mem-ug/AutoDiscovery.HowAutoDiscoveryWorks.html 8 | 9 | # cd /tmp 10 | 11 | # Change `7.4` from below with your PHP version. 12 | # For a list of PHP versions, check the following link (you should be logged in your AWS account): https://console.aws.amazon.com/elasticache/home#client-download: 13 | # Follow the official repo here: https://github.com/awslabs/aws-elasticache-cluster-client-memcached-for-php 14 | # The following examples are for both ARM and X86, so make sure to use the right version by uncommenting one of the following lines 15 | # or by replacing your own PHP version with it. 16 | 17 | # wget https://elasticache-downloads.s3.amazonaws.com/ClusterClient/PHP-7.4/latest-64bit-arm -O memcached.tar.gz 18 | # wget https://elasticache-downloads.s3.amazonaws.com/ClusterClient/PHP-7.4/latest-64bit-X86 -O memcached.tar.gz 19 | 20 | # tar -zxvf memcached.tar.gz 21 | 22 | # sudo mv amazon-elasticache-cluster-client.so /usr/lib64/php/modules/ 23 | 24 | # sudo grep -q '^extension' /etc/php.d/50-memcached.ini && sudo sed -i 's/^extension.*/extension=amazon-elasticache-cluster-client.so/' /etc/php.d/50-memcached.ini || echo 'extension=amazon-elasticache-cluster-client.so' | sudo tee -a /etc/php.d/50-memcached.ini 25 | 26 | # rm -rf memcached.tar.gz 27 | -------------------------------------------------------------------------------- /.platform/hooks/postdeploy/x_optimize_php.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This file will make sure that will set the max processes and spare processes 4 | # according to the details provided by this machine instance. 5 | 6 | DEFAULT_PROCESS_MEMORY="120" 7 | MAX_REQUESTS="500" 8 | 9 | PROCESS_MAX_MB=$(ps --no-headers -o "rss,cmd" -C php-fpm | awk '{ sum+=$1 } END { printf ("%d\n", sum/NR/1024) }') || $DEFAULT_PROCESS_MEMORY 10 | 11 | VCPU_CORES=$(($(lscpu | awk '/^CPU\(s\)/{ print $2 }'))) 12 | 13 | TOTAL_MEMORY_IN_KB=$(free | awk '/^Mem:/{print $2}') 14 | USED_MEMORY_IN_KB=$(free | awk '/^Mem:/{print $3}') 15 | FREE_MEMORY_IN_KB=$(free | awk '/^Mem:/{print $4}') 16 | 17 | TOTAL_MEMORY_IN_MB=$(($TOTAL_MEMORY_IN_KB / 1024)) 18 | USED_MEMORY_IN_MB=$(($USED_MEMORY_IN_KB / 1024)) 19 | FREE_MEMORY_IN_MB=$(($FREE_MEMORY_IN_KB / 1024)) 20 | 21 | MAX_CHILDREN=$(($FREE_MEMORY_IN_MB / $PROCESS_MAX_MB)) 22 | 23 | # Optimal would be to have at least 1/4th of the children filled with children waiting to serve requests. 24 | START_SERVERS=$(($MAX_CHILDREN / 4)) 25 | MIN_SPARE_SERVERS=$(($MAX_CHILDREN / 4)) 26 | 27 | # Optimal would be to have at most 3/4ths of the children filled with children waiting to serve requests. 28 | MAX_SPARE_SERVERS=$(((3 * $MAX_CHILDREN) / 4)) 29 | 30 | sudo sed -i "s|pm.max_children.*|pm.max_children = $MAX_CHILDREN|g" /etc/php-fpm.d/www.conf 31 | sudo sed -i "s|pm.start_servers.*|pm.start_servers = $START_SERVERS|g" /etc/php-fpm.d/www.conf 32 | sudo sed -i "s|pm.min_spare_servers.*|pm.min_spare_servers = $MIN_SPARE_SERVERS|g" /etc/php-fpm.d/www.conf 33 | sudo sed -i "s|pm.max_spare_servers.*|pm.max_spare_servers = $MAX_SPARE_SERVERS|g" /etc/php-fpm.d/www.conf 34 | 35 | printf "\npm.max_requests = $MAX_REQUESTS" | sudo tee -a /etc/php-fpm.d/www.conf 36 | 37 | # Restarting the services afterwards. 38 | sudo systemctl restart php-fpm.service 39 | sudo systemctl restart nginx.service 40 | -------------------------------------------------------------------------------- /.ebextensions/02_logs_cloudwatch_imds2.config: -------------------------------------------------------------------------------- 1 | # The following file configures a new version of cloudwatch logs agent that uses IMDSv2 instead of IMDSv1 2 | # to push the /var/app/current/storage/logs/*.log files to a Log Group in CloudWatch Logs. 3 | 4 | # This version of CloudWatch Logs agent is installed automatically on Amazon Linux 2 instances. 5 | 6 | # The old version of cloudwatch doesn't support Metadata Service Version 2 (IMDSv2) and it's recommended to use a new one 7 | # Link to reference: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/QuickStartEC2Instance.html 8 | 9 | # You can then see the CloudWatch Logs by accessing the AWS CloudWatch Console and clicking 10 | # the "Logs" link on the left. The Log Group name will follow this format: 11 | # /aws/elasticbeanstalk// 12 | 13 | # For more information about "Log Streaming" feature, see: 14 | # https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/QuickStartEC2Instance.html 15 | 16 | # Usually, the option_settings with enabled Stream Logs should be uncommented if you are not using IaaC, like Chef or Terraform. 17 | # If you created the environment manually, uncomment the following lines and set the amount of retention in days. 18 | 19 | # Before uncommenting this, please update to match yours, or name it as you wish 20 | 21 | # option_settings: 22 | # - namespace: aws:elasticbeanstalk:cloudwatch:logs 23 | # option_name: StreamLogs 24 | # value: true 25 | # - namespace: aws:elasticbeanstalk:cloudwatch:logs 26 | # option_name: DeleteOnTerminate 27 | # value: false 28 | # - namespace: aws:elasticbeanstalk:cloudwatch:logs 29 | # option_name: RetentionInDays 30 | # value: 7 31 | 32 | # files: 33 | # "/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/laravel_logs.json" : 34 | # mode: "000644" 35 | # owner: root 36 | # group: root 37 | # content: | 38 | # { 39 | # "logs": { 40 | # "logs_collected": { 41 | # "files": { 42 | # "collect_list": [ 43 | # { 44 | # "file_path": "/var/app/current/storage/logs/*.log", 45 | # "log_group_name": "/aws/elasticbeanstalk//var/app/current/storage/logs/", 46 | # "log_stream_name": "{instance_id}" 47 | # } 48 | # ] 49 | # } 50 | # } 51 | # } 52 | # } 53 | 54 | # container_commands: 55 | # start_cloudwatch_agent: 56 | # command: /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/laravel_logs.json 57 | -------------------------------------------------------------------------------- /.ebextensions/01_logs_streamtocloudwatch.config: -------------------------------------------------------------------------------- 1 | # The following file installs and configures the AWS CloudWatch Logs agent 2 | # to push the /var/app/current/storage/logs/laravel.log file to a Log Group in CloudWatch Logs. 3 | 4 | # You can then see the CloudWatch Logs by accessing the AWS CloudWatch Console and clicking 5 | # the "Logs" link on the left. The Log Group name will follow this format: 6 | # /aws/elasticbeanstalk// 7 | 8 | ## For more information about "Log Streaming" feature, see: 9 | ## http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/AWSHowTo.cloudwatchlogs.html 10 | 11 | ## This file was templated based on the following example: 12 | ## https://github.com/awsdocs/elastic-beanstalk-samples/blob/master/configuration-files/aws-provided/instance-configuration/logs-streamtocloudwatch-linux.config 13 | 14 | # Usually, the option_settings with enabled Stream Logs should be uncommented if you are not using IaaC, like Chef or Terraform. 15 | # If you created the environment manually, uncomment the following lines and set the amount of retention in days. 16 | 17 | # option_settings: 18 | # - namespace: aws:elasticbeanstalk:cloudwatch:logs 19 | # option_name: StreamLogs 20 | # value: true 21 | # - namespace: aws:elasticbeanstalk:cloudwatch:logs 22 | # option_name: DeleteOnTerminate 23 | # value: false 24 | # - namespace: aws:elasticbeanstalk:cloudwatch:logs 25 | # option_name: RetentionInDays 26 | # value: 7 27 | 28 | packages: 29 | yum: 30 | awslogs: [] 31 | 32 | # To enable the AWS logs configuration, uncomment the following lines. This will configure your EC2 instance 33 | # to install and configure the AWS Cloudwatch Logs agent to stream your storage/logs/laravel.log file. 34 | 35 | # files: 36 | # "/etc/awslogs/awscli.conf" : 37 | # mode: "000600" 38 | # owner: root 39 | # group: root 40 | # content: | 41 | # [plugins] 42 | # cwlogs = cwlogs 43 | # [default] 44 | # region = `{"Ref":"AWS::Region"}` 45 | # 46 | # "/etc/awslogs/awslogs.conf" : 47 | # mode: "000600" 48 | # owner: root 49 | # group: root 50 | # content: | 51 | # [general] 52 | # state_file = /var/lib/awslogs/agent-state 53 | # 54 | # "/etc/awslogs/config/logs.conf" : 55 | # mode: "000600" 56 | # owner: root 57 | # group: root 58 | # content: | 59 | # [/var/app/current/storage/logs] 60 | # datetime_format = %Y-%m-%d %H:%M:%S 61 | # log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/app/current/storage/logs"]]}` 62 | # log_stream_name = {instance_id} 63 | # timestamp_format = '[%Y-%m-%d %H:%M:%S]' 64 | # file = /var/app/current/storage/logs/laravel.log 65 | # buffer_duration = 5000 66 | # use_gzip_http_content_encoding = true 67 | # multi_line_start_pattern = {datetime_format} 68 | 69 | # commands: 70 | # "01_enable_aws_log_daemon": 71 | # command: systemctl enable awslogsd.service 72 | # "02_restart_aws_log_daemon": 73 | # command: systemctl restart awslogsd 74 | -------------------------------------------------------------------------------- /.platform/files/php.ini: -------------------------------------------------------------------------------- 1 | ; Determines if Zend OPCache is enabled 2 | opcache.enable=1 3 | 4 | ; Determines if Zend OPCache is enabled for the CLI version of PHP 5 | opcache.enable_cli=1 6 | 7 | ; The OPcache shared memory storage size. 8 | opcache.memory_consumption=512 9 | 10 | ; The amount of memory for interned strings in Mbytes. 11 | opcache.interned_strings_buffer=128 12 | 13 | ; The maximum number of keys (scripts) in the OPcache hash table. 14 | ; Only numbers between 200 and 1000000 are allowed. 15 | opcache.max_accelerated_files=1000000 16 | 17 | ; maximum memory allocated to store the results 18 | realpath_cache_size=8192K 19 | 20 | ; save the results for 10 minutes (600 seconds) 21 | realpath_cache_ttl=600 22 | 23 | ; The maximum percentage of "wasted" memory until a restart is scheduled. 24 | opcache.max_wasted_percentage=5 25 | 26 | ; When this directive is enabled, the OPcache appends the current working 27 | ; directory to the script key, thus eliminating possible collisions between 28 | ; files with the same name (basename). Disabling the directive improves 29 | ; performance, but may break existing applications. 30 | ;opcache.use_cwd=1 31 | 32 | ; When disabled, you must reset the OPcache manually or restart the 33 | ; webserver for changes to the filesystem to take effect. 34 | opcache.validate_timestamps=0 35 | 36 | ; How often (in seconds) to check file timestamps for changes to the shared 37 | ; memory storage allocation. ("1" means validate once per second, but only 38 | ; once per request. "0" means always validate) 39 | opcache.revalidate_freq=0 40 | 41 | ; Enables or disables file search in include_path optimization 42 | ;opcache.revalidate_path=0 43 | 44 | ; If disabled, all PHPDoc comments are dropped from the code to reduce the 45 | ; size of the optimized code. 46 | ;opcache.save_comments=1 47 | 48 | ; If enabled, a fast shutdown sequence is used for the accelerated code 49 | ; Depending on the used Memory Manager this may cause some incompatibilities. 50 | opcache.fast_shutdown=1 51 | 52 | ; Allow file existence override (file_exists, etc.) performance feature. 53 | ;opcache.enable_file_override=0 54 | 55 | ; A bitmask, where each bit enables or disables the appropriate OPcache 56 | ; passes 57 | ;opcache.optimization_level=0xffffffff 58 | 59 | ;opcache.inherited_hack=1 60 | ;opcache.dups_fix=0 61 | 62 | ; The location of the OPcache blacklist file (wildcards allowed). 63 | ; Each OPcache blacklist file is a text file that holds the names of files 64 | ; that should not be accelerated. 65 | opcache.blacklist_filename=/etc/php-*/opcache*.blacklist 66 | 67 | ; Allows exclusion of large files from being cached. By default all files 68 | ; are cached. 69 | ;opcache.max_file_size=0 70 | 71 | ; Check the cache checksum each N requests. 72 | ; The default value of "0" means that the checks are disabled. 73 | ;opcache.consistency_checks=0 74 | 75 | ; How long to wait (in seconds) for a scheduled restart to begin if the cache 76 | ; is not being accessed. 77 | ;opcache.force_restart_timeout=180 78 | 79 | ; OPcache error_log file name. Empty string assumes "stderr". 80 | ;opcache.error_log= 81 | 82 | ; All OPcache errors go to the Web server log. 83 | ; By default, only fatal errors (level 0) or errors (level 1) are logged. 84 | ; You can also enable warnings (level 2), info messages (level 3) or 85 | ; debug messages (level 4). 86 | ;opcache.log_verbosity_level=1 87 | 88 | ; Preferred Shared Memory back-end. Leave empty and let the system decide. 89 | ;opcache.preferred_memory_model= 90 | 91 | ; Protect the shared memory from unexpected writing during script execution. 92 | ; Useful for internal debugging only. 93 | ;opcache.protect_memory=0 94 | 95 | ; Allows calling OPcache API functions only from PHP scripts which path is 96 | ; started from specified string. The default "" means no restriction 97 | ;opcache.restrict_api= 98 | 99 | ; Enables and sets the second level cache directory. 100 | ; It should improve performance when SHM memory is full, at server restart or 101 | ; SHM reset. The default "" disables file based caching. 102 | ; RPM note : file cache directory must be owned by process owner 103 | ; for mod_php, see /etc/httpd/conf.d/php.conf 104 | ; for php-fpm, see /etc/php-fpm.d/*conf 105 | ;opcache.file_cache= 106 | 107 | ; Enables or disables opcode caching in shared memory. 108 | ;opcache.file_cache_only=0 109 | 110 | ; Enables or disables checksum validation when script loaded from file cache. 111 | ;opcache.file_cache_consistency_checks=1 112 | 113 | ; Implies opcache.file_cache_only=1 for a certain process that failed to 114 | ; reattach to the shared memory (for Windows only). Explicitly enabled file 115 | ; cache is required. 116 | ;opcache.file_cache_fallback=1 117 | 118 | ; Validate cached file permissions. 119 | ; Leads OPcache to check file readability on each access to cached file. 120 | ; This directive should be enabled in shared hosting environment, when few 121 | ; users (PHP-FPM pools) reuse the common OPcache shared memory. 122 | ;opcache.validate_permission=0 123 | 124 | ; Prevent name collisions in chroot'ed environment. 125 | ; This directive prevents file name collisions in different "chroot" 126 | ; environments. It should be enabled for sites that may serve requests in 127 | ; different "chroot" environments. 128 | ;opcache.validate_root=0 129 | 130 | ; Enables or disables copying of PHP code (text segment) into HUGE PAGES. 131 | ; This should improve performance, but requires appropriate OS configuration. 132 | opcache.huge_code_pages=1 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Elastic Beanstalk 2 | 3 | Laravel EB is a sample configuration to help you deploy a Laravel app on an AWS Elastic Beanstalk PHP environment without any super rockety-like knowledge. 4 | 5 | ## 🤝 Supporting 6 | 7 | If you are using one or more Renoki Co. open-source packages in your production apps, in presentation demos, hobby projects, school projects or so, spread some kind words about our work or sponsor our work via Patreon. 📦 8 | 9 | You will sometimes get exclusive content on tips about Laravel, AWS or Kubernetes on Patreon and some early-access to projects or packages. 10 | 11 | [](https://www.patreon.com/bePatron?u=10965171) 12 | 13 | # Amazon Linux 2 14 | 15 | This branch is working with the new Amazon Linux 2. 16 | 17 | It's highly recommended to upgrade to the Amazon Linux 2 version, since it's faster and more secure. ([see AWS's announcement](https://aws.amazon.com/about-aws/whats-new/2020/04/aws-elastic-beanstalk-announces-general-availability-of-amazon-linux-2-based-docker-corretto-and-python-platforms/)) 18 | 19 | To upgrade to AL2 from your Amazon Linux AMI, please see the [Amazon Linux 2 migration guide](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.migration-al.html) 20 | 21 | If you still work with Amazon Linux AMI, please switch to the [amazon-ami branch](../../tree/amazon-ami) to read the older docs. 22 | 23 | # Packaged 24 | 25 | The sample configuration comes with: 26 | 27 | - Automation for copying .env file from AWS EB's S3 bucket (so you won't have to add the env variables from the AWS Console) 28 | - Laravel Artisan Scheduler CRON configuration 29 | - Supervisor for Queues 30 | - HTTP to HTTPS support 31 | - Nginx configuration support 32 | - Chromium binary 33 | 34 | # Updating 35 | 36 | The repo works with semantic versioning, so please check the [Releases](../../releases) page for latest updates & changes. 37 | 38 | # Installation 39 | 40 | Clone the repo and drop the `.ebextensions` and `.platform` folders in your root project. 41 | 42 | Make sure that the .sh files from the `.platform` folder are executable before deploying your project: 43 | 44 | ```bash 45 | $ chmod +x .platform/hooks/prebuild/*.sh 46 | ``` 47 | 48 | ```bash 49 | $ chmod +x .platform/hooks/predeploy/*.sh 50 | ``` 51 | 52 | ```bash 53 | $ chmod +x .platform/hooks/postdeploy/*.sh 54 | ``` 55 | 56 | ```bash 57 | $ chmod +x .platform/scripts/*.sh 58 | ``` 59 | 60 | # AWS EB Should-Know 61 | 62 | ## Deployment Stages 63 | 64 | Elastic Beanstalk helps you deploy apps while keeping them up, so you won't have to turn your app down during deployments. For this, there are two paths: 65 | 66 | - `/var/app/staging` that holds the app between deployments. This folder is not pointed to until the deployment finishes 67 | - `/var/app/current` that holds the live app and that serves requests actively 68 | 69 | Each deploy makes you lose everything you have in the `current` folder. **DO NOT** rely on local storage, use S3 instead with CloudFront to avoid data loss and speed up the things. 70 | 71 | ## .ebextensions/ 72 | 73 | The `.ebextensions` folder contains information about which commands to run during the deployment, such as migrations or copying files in the instance. 74 | 75 | In this repo, see `01_deploy.config` for a list of commands that will be ran upon deployment. 76 | 77 | On the other hand, the `00_copy_env_file.config` will copy the `.env` file from your S3 bucket and put it temporarily in the `/tmp` folder, to later be copied in the deployment process. 78 | 79 | Please open the files to see further comments on the particular sections and change them. 80 | 81 | ## .platform/ 82 | 83 | The `.platform` folder contains mostly shell scripts that will be ran during the deployment, like configuring or installing software, like supervisor, or running scripts after the deployment finished. 84 | 85 | Consider looking in the `files/` folder for Supervisor and PHP custom configurations that will be automatically be applied, out-of-the-box. 86 | 87 | Additionally, the `hooks/`folder contains scripts that will be ran during various deployment stages. All scripts contain comments about details and best practices, so take a look. 88 | 89 | Please refer to this scheme to understand the execution workflow: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/images/platforms-linux-extend-order.png 90 | 91 | # Use Cases 92 | 93 | ## HTTP to HTTPS 94 | 95 | Check the `.platform/nginx/conf.d/elasticbeanstalk/https.conf` file to enable HTTP to HTTPS redirect. 96 | 97 | ## Laravel Passport 98 | 99 | Since Laravel Passport uses local storage to keep the public and private key, there is no way of using this method. Instead, you might use what this PR added: https://github.com/laravel/passport/pull/683 100 | 101 | In your `.env` file, add the following variables **and make sure that there is a `\\n` for each newline**: 102 | 103 | ``` 104 | PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\\nMIIJJwIBAAKCAgEAw3KPag...\\n-----END RSA PRIVATE KEY-----" 105 | PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\\nMIICIjANBgkqhkiG9w0BAQEFAAOC...\\n-----END PUBLIC KEY-----\\n" 106 | ``` 107 | 108 | ## Spatie Media Library & other Imagick-based packages 109 | 110 | Some packages require Imagick to run. 111 | 112 | To enable Imagick installation on the instance via Amazon Linux Extras, check `install_imagick.sh` file for details. 113 | 114 | ## Memcached Auto Discovery 115 | 116 | Memcached Auto Discovery for AWS Memcached is a PHP extension that replace the default Memcached extension, in order to use Memcached clusters in multi-node mode. 117 | 118 | Plese see `install_memcached_discovery.sh` file to enable the installation for your PHP version. 119 | 120 | For the Laravel app, edit your `memcached` connection in `cache.php` to make it suitable for multi-node configuration: 121 | 122 | ```php 123 | 'memcached' => [ 124 | 'driver' => 'memcached', 125 | 126 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID', 1), // make sure you also set a default to the persistent_id 127 | 128 | 'options' => array_merge([ 129 | Memcached::OPT_DISTRIBUTION => Memcached::DISTRIBUTION_CONSISTENT, 130 | Memcached::OPT_LIBKETAMA_COMPATIBLE => true, 131 | Memcached::OPT_SERIALIZER => Memcached::SERIALIZER_PHP, 132 | ], in_array(env('APP_ENV'), ['production', 'staging']) ? [ 133 | Memcached::OPT_CLIENT_MODE => Memcached::DYNAMIC_CLIENT_MODE, 134 | ] : []), 135 | ] 136 | ``` 137 | 138 | **For production & staging workloads (when AWS Elasticache is used), `Memcached::OPT_CLIENT_MODE` should be set. `OPT_CLIENT_MODE` and `DYNAMIC_CLIENT_MODE` are Memcached Auto Discovery extension-related constants, not available in the default Memcached extension.** 139 | 140 | ## Chromium binary support 141 | 142 | Some Laravel apps, like crawlers, might need a Chrome binary to run upon. A good example is [spatie/browsershot](https://github.com/spatie/browsershot#custom-chromechromium-executable-path). It lets you take browser screenshots using PHP and a Chromium binary. 143 | 144 | To install Chromium, seek for the `install_latest_chromium_binary.sh` script and uncomment the code. 145 | 146 | The binary can be then accessed from `/usr/bin/google-chrome-stable` 147 | 148 | ## Run on Spot Instances 149 | 150 | Spot instances are the cheapest EC2 instances from AWS, but they can be terminated 151 | anytime. Please refer to this to understand how they can be used: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html 152 | 153 | Spot instances can be configured from the console. Check out AWS announcement: https://aws.amazon.com/about-aws/whats-new/2019/11/aws-elastic-beanstalk-adds-support-for-amazon-ec2-spot-instances/ 154 | 155 | # Multi-environment 156 | 157 | Sometimes you might have more than one environment, for example production and staging. Having duplicate configuration can be tricky, but you can workaround this problem by seeking a CloudFromation-like appoach as presented in one of the issues: https://github.com/rennokki/laravel-aws-eb/issues/30#issuecomment-693154271 158 | 159 | The idea behind it is to use, for example, `{"Ref": "AWSEBEnvironmentName"}` value to concatenate to the name of the .env file that should be downloaded from S3. This way, you can have `.env.staging-app` if your AWS EB environment is named `staging-app`. 160 | 161 | # Deploying from the CI/CD Pipeline 162 | 163 | To deploy to the EB environment, you have two choices: 164 | 165 | - Archive the ZIP by your own and upload it. 166 | - Pull from git, and use AWS EB CLI in the current folder (with no additional ZIP-ing) 167 | 168 | AWS EB CLI make use of `.gitignore` and `.ebignore`. The only pre-configuration you need is to add the following environment variables 169 | to your CI/CD machine: 170 | 171 | - `AWS_ACCESS_KEY_ID` 172 | - `AWS_SECRET_ACCESS_KEY` 173 | - `AWS_EB_REGION` 174 | 175 | If you use a dockerized CI/CD pipeline (like Gitlab CI), you can make use of the `renokico/aws-cli:latest` image. 176 | 177 | The following commands let you deploy an app on a certain environment within Gitlab CI on tag creation, for example: 178 | 179 | ```bash 180 | $ git checkout $CI_COMMIT_TAG 181 | ``` 182 | 183 | ```bash 184 | $ eb init --region=$AWS_EB_REGION --platform=php [project-name] 185 | ``` 186 | 187 | ```bash 188 | $ eb use [environment-name] 189 | ``` 190 | 191 | ```bash 192 | $ eb deploy [environment-name] --label=$CI_COMMIT_TAG 193 | ``` 194 | --------------------------------------------------------------------------------