Overview
Introduction
Greetings, fellow artisan, and welcome to LaraSurf!
Your journey of rapidly implementing, deploying, and iterating upon your next Laravel project begins here.
While we highly recommend reading the documentation in its entirety before getting started, you can check out the TL;DR Checklist to guide you through local setup and launch within 90 minutes.
What is LaraSurf?
LaraSurf is an opinionated end-to-end solution for Laravel projects that assists with local development (using Docker), cloud infrastructure (on AWS), and CI/CD pipelines (using CircleCI).
An important thing to note is that LaraSurf was built upon Laravel but is not part of the official Laravel ecosystem.
While the Laravel ecosystem and what LaraSurf accomplishes may have some overlap, LaraSurf focuses on automating integrations between multiple technologies and providing a developer with everything they need to start a new project with cloud environments in mind.
An overview of the LaraSurf solution is outlined in the Venn diagram below:
Why?
LaraSurf was born out of the desire to quickly spin up and deploy Laravel applications without having to repeat routine tasks such as setting up local and cloud environments and CI/CD pipelines (every. single. time.). It's a turnkey solution for hitting the ground running with a fresh Laravel application in a matter of hours instead of days (or weeks).
Mission
To provide an end-to-end solution for new small to medium sized Laravel projects, lowering the barrier of entry for making it to the production and iteration phases of a project, as well as providing tools to enable rapid prototyping and implementation.
Audience
LaraSurf is intended for Laravel developers looking to kickstart their next application and simplify environment prerequisites and interactions.
To feel comfortable using LaraSurf, you should have at least moderate familiarity with the following concepts:
- Using a Terminal
- Laravel Development
- Containers
- Common Amazon Web Services
Guiding Principles
LaraSurf was created with the following principles in mind:
- Customizing the infrastructure and CI/CD pipelines for LaraSurf projects should be supported
- Laravel-provided authentication presets should be supported
- Natively installed software for a local development environment should be limited as much as possible
- AWS and CircleCI web console interaction should be limited as much as possible
- Automation should be heavily preferred when possible
- Application environments should match as closely as possible
Features
Project Generation
- Specify project environment(s)
- Local
- Local and Production
- Local, Stage, and Production
- Ready-to-go dockerized local development environment
- Update .gitignore
- Optionally specify an authentication preset
- Optionally install Laravel Dusk
- Optionally install (and run) Laravel IDE Helper
- Optionally generate and install a local TLS certificate
- Automatically install composer dependencies
- Automatically configure the project for using AWS services
- Automatically install and build JavaScript dependencies
- Automatically install the LaraSurf development package
- Automatically create environment branches
- Automatically spin up the local environment
Local Development
- Preconfigured Docker-Compose services
- NGINX service
- PHP-FPM service (PHP 8.1)
- MySQL 8 service
- Redis service
- LocalStack service (local AWS)
- AWS CLI service
- MailPit service (local mock SMTP server)
- LaraSurf CLI tool
- Run composer commands
- Run yarn commands
- Run artisan commands
- Run AWS CLI commands against LocalStack
- Refresh the database and local S3 bucket
- Run PHPUnit
- Run Laravel Dusk
Cloud Infrastructure
- Manage application secrets in SSM Parameter Store
- Create container image repositories
- Deploy cloud infrastructure
- Enable SES sending and DKIM verification
- Issue and verify ACM certificates
- Manage application and database access
- Run artisan commands on cloud environments
- Run artisan tinker on cloud environments
CI/CD
- Build and publish container images for the application and webserver
- Run feature and unit tests
- Run browser tests (if applicable)
- Scan for known vulnerabilities in container images and dependencies
- Deploy application changes upon merging into an environment branch
Technology
Application Assumptions
It is assumed that every LaraSurf project will have the following needs and/or characteristics:
Version Control
- A local development environment and optional stage or stage and production environments
- Git for version control and GitHub for the repositories origin
- A simplified version of GitFlow for branch management
- See the Project Lifecycle
Continuous Integration and Delivery
- CircleCI for CI/CD pipleines
- Unit and feature tests must pass before progressing through the pipeline
- Scanning for known security vulnerabilities in container images and dependencies
- Automated image building and deployments for specific branches (stage or main)
Infrastructure and Software
- TLS for HTTPS ingress
- Terminated at NGNIX level locally
- Terminated at ELB level on all other environments
- HTTP redirected to HTTPS
- MySQL 8 for the database
- Official Docker image locally
- RDS on AWS
- S3 for dynamic file storage
- LocalStack service locally
- SQS for queued messages
- LocalStack service locally
- Scheduled tasks
- Via CloudWatch events for non-local environments
- Caching using Redis
- Official Docker image locally
- ElastiCache on AWS
- Email sending
- Using MailPit locally
- Using MailTrap for stage
- Using SES for production
- Application Secrets
- Via .env file locally
- Via Environment Variables injected from SSM Parameter Store for non-local environments
- DNS entry for the Load Balancer
- Route53 on AWS
- Modest AutoScaling on ECS Fargate
- Non-local environments
Stack
LaraSurf, in one form or another, uses the following technologies.
This may not be an exhaustive list.
- Laravel 10
- MailPit
- NGINX
- PHP-FPM
- PHP 8.1
- MySQL 8
- Redis
- LocalStack
- Selenium
- Docker and Docker-Compose
- CircleCI
- AWS CLI v2
- Amazon Web Services
- Elastic Container Service
- Elastic Container Registry
- Simple Storage Service
- Simple Queue Service
- Relational Database Service
- Simple Email Service
- ElastiCache
- CloudWatch
- CloudWatch Logs
- Amazon Certificate Manager
- Route53
- Systems Manager Parameter Store
Project Lifecycle
Introduction
The recommend project lifecycle for a LaraSurf project is intended to be as simple as possible.
This document assumes you intend to have both a Stage and Production environment. If you only want Production, the same concepts apply without a Stage environment. If you want a local environment, you can ignore steps related to other environments.
The diagram below outlines the typical phases of a LaraSurf project, from project generation to production, and beyond to iteration.
Project Generation
LaraSurf projects begin by running a custom command in your terminal which cURLs a shell script. At the end of the project generation process you will be setup to immediately begin implementation (or deploy the default project to stage/production if you so choose).
For more information, see the Project Generation documentation.
Infrastructure Deployment
Infrastructure for LaraSurf projects can be deployed using a few simple commands, with the bulk of the heavy lifting done by a predefined AWS CloudFormation template.
Deployment of infrastructure can be done at any time; you are free to work on your project locally for as long as you like before creating infrastructure for and deploying to Stage and/or Production.
Ingress into either environment's application or database can be changed at will using single commands.
For more information, see the Cloud Environments documentation.
Stage
The Stage environment is intended to be the pre-production environment where testing for Quality Assurance takes place. The application and webserver versions deployed to the Stage environment represent how the system will behave on Production after the next Production deployment.
A Stage environment is also optional. If you intended to have only a Production environment (or only a local environment), you are free to specify this during your project generation process.
The default LaraSurf setup assumes you will be using a mock SMTP server such as MailTrap and configures your application accordingly.
You are of course free to send live emails from your Stage environment using SES or any other mechanism by Laravel instead.
Production
The Production environment is your live application accessible to your target audience. Typically this environment will be publicly accessible, but it doesn't have to be.
Local Development
Implementation for LaraSurf projects is intended to take place on a local machine with Docker support. Using Docker Compose to define local services, a complete development environment for LaraSurf projects is available out of the box. This setup includes the necessary tools to locally run and interact with your application with room to be customized if needed.
For more information, see the Local Development documentation.
Branching Pattern
The recommended branching pattern for LaraSurf projects is a simplified version of GitFlow.
Depending on your configured environments, there are up to three branches created for you as part of the project generation process.
- For Local only projects, the main branch is created
- For Local and Production projects, the main and develop branches are created
- For Local, Stage, and Production projects, the main, stage and develop branches are created
The purpose of each branch is as follows:
- main is what's currently deployed to the Production environment
- stage is what's currently deployed to the Stage environment
- develop is for completed features and bugfixes that you don't want deployed to the Stage environment yet
In addition the these three branches, additional branches are created as part of the development process:
- feature/* branches are for adding new features to your application
- branched off of and merged back into the develop branch
- bugfix/* branches are for fixing bugs in your application that either haven't made it to the Production environment yet or aren't a high priority
- branched off of the develop or stage branch and merged into either develop or both stage and develop depending on the parent branch
- hotfix/* branches are for fixing critical bugs that exist in the Production environment
- branched off of and merged back into the main branch
While it is recommended every merge take place on GitHub to allow for easier code review, it is not a requirement.
The diagram below outlines an example branch history as time moves forwards. Assume the cloud infrastructure for both the Stage and Production environments was deployed immediately after project generation.
CircleCI Pipeline
LaraSurf projects use CircleCI workflows to automatically run Unit and Feature tests, scan for known vulnerabilities, build container images, publish container images, and deploy new versions of the application and webserver.
The default CircleCI workflows will work for most use cases but you are free to modify the configuration file to suit any additional needs.
By default, Unit and Feature tests (and Dusk tests if applicable) are run on every branch that is pushed up to GitHub. In addition, for the stage and main branches, container images are built, scanned for known vulnerabilities, published to AWS Elastic Container Registry, and deployed to the appropriate environment.
For more information, see the CircleCI Pipeline documentation.
Prerequisites
LaraSurf currently only supports new projects that have been generated by LaraSurf (though you are free to look through the source if you want inspiration for an existing project).
Accounts
- A GitHub account is required for hosting your Git repository
- A configured SSH key is required for cloning the template project
- A Docker Hub account is required to pull container images
- A CircleCI account is required for running CI/CD pipelines
- An AWS account is required for cloud infrastructure
- An MailTrap account is optional, but recommended for a Stage environment
Software
- You must be running MacOS or WSL2 on Windows
- You must be using bash or zsh
- For MacOS, iTerm2 is recommended
- cURL must be installed
- (likely already installed)
- Netcat must be installed
- (likely already installed)
- Docker Desktop must be installed
- The v2 Docker-Compose CLI should not be enabled
- Git must be installed
- A global user name must be configured
- A global user email must be configured
- Mkcert is required if you want local TLS support
- On Windows, the executable must be named mkcert.exe and be accessible via your system PATH
Project Generation
The LaraSurf project generation process is designed to allow artisans to hit the ground running. Monotonous tasks for project setup have been automated; just kick off the project generation and go make a cup of coffee. Within 10-15 minutes your development environment will be ready and implementation can begin.
Generation Steps
LaraSurf's project generation process is nothing more than a command run in your terminal that:
- cURLs a shell script with specific project generation options
- changes directories into the new project directory
- displays the time elapsed
- runs git status
Within the cURLed shell script, there are three main steps to the process:
- Pre-Installation
- Installation
- Post-Installation
Pre-Installation
- First, LaraSurf will verify needed software is installed and all needed ports for Docker Compose services are open
- Then git is used to clone a pre-dockerized repository template from Github
- Next required Docker images are pulled
- Finally, the PHP-FPM and NGINX container images are built
Installation
- Installation begins with the creation of a new Laravel 10 project using Composer
- After the project is created, Composer dependencies are installed
- If specified, the selected front end stack is installed
- AWS dependencies are installed
- If specified, the barryvdh/laravel-ide-helper package is installed
- If specified, the laravel/dusk package is installed
- Then, if applicable, front end dependencies are installed and transpiled
- Finally, the larasurf/larasurf package is installed
Post-Installation
- As the first step of post-installation, a local TLS certificate is created and trusted if local TLS is specified in the project generation options
- Next, a default larasurf.json configuration file is created
- Both the .env and .env.example files are updated to reflect changes to configuration
- If applicable, a CircleCI configuration file is published
- If applicable, an AWS CloudFormation template is published
- The .gitignore file is overwritten
- A health check route is created
- The TrustProxies middleware is updated to trust all proxies
- The local NGINX configuration is updated if local TLS was selected
- If applicable, a default code style definition file is published
- The docker-compose.yml file is updated if Laravel Dusk was selected
- Database migrations are run for the local MySQL database
- If applicable, IDE Helper files are generated
- If applicable, code style fixing is run
- The LaraSurf CLI script is updated to be executable
- A git repository is initialized, an initial commit is made, the master branch is renamed to main, and if applicable, a stage and/or develop branch is created
- Finally, if required, the NGINX container image is rebuilt
Generating a New Project
In order to generate a new LaraSurf project, visit larasurf.com/new in your browser.
Enter your project name, select your desired configuration options, click "Generate", copy the displayed command into your terminal (with a working directory of where you want the new project directory created), and execute the command.
You may be prompted for your SSH key passphrase (if configured) and/or your password during the project generation process.
Shell Alias
If this is your first time generating a project with LaraSurf, after the project generation completes the command surf should be aliased to: vendor/bin/surf.
You may use the following command to create this alias:
if [ -n "$ZSH_VERSION" ]; then echo "alias surf='vendor/bin/surf'" >> ~/.zshenv && source ~/.zshenv; elif [ -f ~/.bash_profile ]; then echo "alias surf='vendor/bin/surf'" >> ~/.bash_profile; else echo "if [ -f ~/.bashrc ]; then source ~/.bashrc; fi" >> ~/.bash_profile && echo "alias surf='vendor/bin/surf'" >> ~/.bash_profile && source ~/.bash_profile; fi
AWS CLI Configuration
For LaraSurf projects, the AWS CLI does not need to be natively installed. Instead, a container image published by AWS is used.
If you have previously configured the AWS CLI on your machine with adequate credentials to create resources within your AWS account, there's nothing more you need to do for this step.
IAM User
If you do not already have access keys for the AWS account you'd like to use for your project, that will be the first step. It is recommended to not use a root account for access and instead to create an IAM user within the AWS web console that has administrator access.
When creating an IAM user, be sure to enable programmatic access and save the Access Key ID and Secret Access Key from the downloadable CSV provided by AWS in a secure location; it will be needed in the next step.
CLI Configuration
Once you have the access keys needed you can configure the AWS CLI by running the following command (requires a shell alias, see the Project Generation documentation for more details):
surf aws configure
You will be prompted for the following pieces of information:
- Your Access Key ID
- Your Secret Access Key
- The default region to use (when not otherwise specified)
- us-east-1 is recommended
- The default output format to use (when not otherwise specified)
- json is recommended
GitHub Repository
In order to run workflows on CircleCI (and to back up your code!), a GitHub repository for your project is required.
New Repository
A new GitHub project can be created within your account or organization here.
By convention, the name of your GitHub repository should match the name of your LaraSurf project specified during project generation (lowercase alphanumeric and hyphens).
If you are unsure of your project name you can run surf config get project-name to get the configured value.
Local Git Configuration
After you've created a repository on GitHub for your project, it is time to configure your local environment to reference GitHub as the remote repository.
The below example is for a hypothetical project called my-project within an organization named my-org. Replace this project name and organization name with the name of your own project and organization/account.
First, configure your local git repository to use your new GitHub repository as the remote repository.
git remote add origin git@github.com:my-org/my-project.git
The Default Branch
Now, push up the develop branch and set it as the upstream branch.
git push -u origin develop
Note that the command above instructs you to push up the develop branch first (not the main branch), which will make GitHub treat the develop branch as the default branch for new pull requests.
CircleCI Configuration
In order for LaraSurf to be able to manage CircleCI environment variables on your behalf, a personal API token needs to be created within the CircleCI web console and configured within your local project.
Ensure you are signed into your CircleCI account before proceeding with the steps below.
Personal API Token
A personal API token for CircleCI first needs to be created and then configured for your LaraSurf project.
Creating the Personal API Token
To create a personal API token, first navigate to the Personal API Tokens page within the user settings of your CircleCI account.
Click the Create New Token button, enter a name for your token (such as "LaraSurf - My Project"), and click Add API Token.
Your new API token will be displayed only once so be sure to copy the token carefully.
Configuring the Token for your Project
After you have created the CircleCI personal API token, you can configure it within your LaraSurf project with the following command:
surf circleci set-api-key
You will be prompted to enter your personal API token.
Note that the prompt for entering your personal API token will not display what you type.
The API token you enter will be saved in your project at .circleci/api-key.txt which should not be checked into source control. Your .gitignore file should already be set up to ignore this file, but a sanity check to confirm never hurts.
Project Setup
Once your personal API token has been configured, you are ready to start building.
Again in the CircleCI web console, navigate to the Projects page for your organization.
Next to the name of your LaraSurf project, click Set Up Project.
Choose the second option indicating your CircleCI configuration is ready and type in develop as the selected branch to start building for.
Click Set Up Project to enable the CircleCI pipeline for your project. This will trigger running through the pipeline for the first time.
CircleCI Pipeline
The preconfigured CircleCI configuration for a LaraSurf project defines a pipeline responsible for:
- Building a container image for your application
- Building a container image for your webserver (on applicable branches)
- Checking PHP code style (if applicable)
- Running unit and feature tests
- Running browser tests (if applicable)
- Publishing container images to AWS ECR (on applicable branches)
- Scanning container images for known vulnerabilities (on applicable branches)
- Deploying container images to your AWS infrastructure (on applicable branches)
- Running migrations post-deployment on your AWS RDS instance (on applicable branches)
You are of course free to customize the published CircleCI configuration file to suit your project's needs.
All Branches
For every branch, the defined pipeline will perform the following steps:
- Checkout the applicable git commit
- Build a container image for the application
- Install system packages and PHP extensions
- Install Composer dependencies
- Install Yarn dependencies
- Transpile front end assets
- Start containers for the application and a MySQL database
- Copy the .env.example file to .env within the application container
- Generate an application key within the application container
- Run code style checks within the application container (if applicable)
- Migrate the test database local to CircleCI
- Run PHPUnit
- If Laravel Dusk is installed:
- Start local application docker-compose services
- Create a local S3 bucket and SQS queue via CloudFormation
- Run Laravel Dusk tests
- Copy screenshots out of the testing container, if any
- Copy log files out of the application container, if any
- Persist any screenshots and/or log files as artifacts
Stage and Main Branches
For the stage and main branches, the defined pipeline will do all of the above as well as:
- Check for required CircleCI environment variables
- Build a container image for the webserver
- Publish the application and webserver container images to AWS ECR
- Scan the application and webserver container images for known vulnerabilities
- This is for reporting only; a found vulnerability does not prevent a deployment
- Deploy the application and webserver container images to AWS ECS
- Run database migrations for the new application version on AWS RDS using AWS ECS
Local Development
Before attempting to run any commands that use the LaraSurf CLI, ensure you have created a shell alias for surf as described here.
The local development environment for LaraSurf projects is centered around local services available through Docker Compose.
These services include:
- LocalStack for local AWS services
- An S3 bucket
- An SQS queue
- Selenium (Chrome) for browser testing (if applicable)
- MailPit for a fake SMTP server
- NGINX for the webserver
- PHP-FPM for the Laravel application
- PHP 8.1
- MySQL 8 for the database
- Redis for the cache
Should your project require additional local services or additional LocalStack services, you are free to modify the Docker Compose configuration as you see fit.
Local Environment
After the project generation process completes your environment will have already been started.
Starting the Local Environment
You can start the local environment's service by running the following command:
surf up
This is equivalent to running docker-compose up -d.
Additionally, if you'd like to start your local environment and monitor output from the environment's services, you can start in attached mode by instead running:
surf up --attach
Stopping the Local Environment
You can stop your local environment's services by running the following command:
surf down
This will destroy your local environment's database and cache.
If you would like to preserve the state of your database and cache, you can instead run the following command:
surf down --preserve
Refreshing the Local Environment
In the event you would like to refresh your local environment, including wiping the database and cache as well as running migrations, you can use the following command:
surf fresh
In addition if you'd also like to run the default database seeder, you can run:
surf fresh --seed
Application Access
If your project supports local TLS, your application can also be accessed at https://localhost.
Otherwise, your application can also be accessed at http://localhost.
If you specified custom ports for your application, append a colon followed by the port number to the URL in your web browser to access your application. For example https://localhost:8080
Email Outbox
MailPit is used as a local fake SMTP server. Unless you specified custom ports for your project, the MailPit UI can be accessed at http://localhost:8025.
Composer Commands
You may execute Composer commands using your local environment's application service with the following syntax:
surf composer <command>
For example:
surf composer --version
Yarn Commands
You may execute Yarn commands using your local environment's application service with the following syntax:
surf yarn <command>
For example:
surf yarn --version
To watch front end assets for changes during local development, use the command:
surf yarn run dev
To build front end assets once, use the command:
surf yarn run build
NPX Commands
You may execute NPX commands using your local environment's application service with the following syntax:
surf npx <command>
For example:
surf npx --version
Node Commands
You may execute Node commands using your local environment's application service with the following syntax:
surf node <command>
For example:
surf node --version
AWS CLI Commands
You may execute AWS CLI commands against your local environment's AWS service with the following syntax:
surf awslocal <command>
For example:
surf awslocal --version
The name of the local S3 bucket is laravel.
Artisan Commands
You may execute Artisan commands using your local environment's application service with the following syntax:
surf artisan <command>
For example:
surf artisan tinker
Queue
With LocalStack having a functioning SQS implementation, the default queue connection for local development on LaraSurf projects is sqs.
This means whenever an interaction with your local application enqueues a job, it must be consumed by a worker by running the following command:
surf artisan queue:work
You can exit the looping queue worker process with Ctrl+C.
MailPit can be accessed locally by visiting localhost over an HTTP connection and specifying the mapped port of the docker-compose service.
By default, this is http://localhost:8025.
PHPUnit
You may execute PHPUnit, with or without any additional options, using the following syntax:
surf test <options>
For example:
surf test
surf test --filter MyTest
Laravel Dusk
You may run Laravel Dusk tests, with or without any additional options, using the following syntax:
surf dusk <options>
For example:
surf dusk
surf dusk --group MyGroup
IDE Helper / Code Style
You may generate updated IDE Helper files as well as fix the code style to match your configured preferences with the following command:
surf fix
Alternatively, if you wish to run only Laravel Pint, you may use the following command syntax:
surf pint <options>
New TLS Certificate
If your local environment's TLS certificate has expired, you can generate a new one, rebuild your webserver container image, and restart your local environment's services with the following sequence of commands:
surf tls
surf rebuild webserver
surf fresh
LaraSurf Configuration
You may manage values in your project's larasurf.json file, which should not be manually edited, using the config command.
Getting a Configuration Value
A configuration value can be retrieved with the following syntax:
surf config get <key>
For example:
surf config get aws-profile
Setting a Configuration Value
A configuration value can be updated with the following syntax:
surf config set <key> <value>
For example:
surf config set aws-profile profile-name
WSL2 Users
Using Windows Subsystem Linux 2 (WSL2) with LaraSurf is supported but there are a couple of things to watch out for.
Vite Development Server
The Vite Development Server may be very slow to respond to requests if your project resides in a Windows filesystem which is mounted within WSL2. Instead, it is recommended to generate (or move) your project within WLS2's filesystem (such as your WSL2 user's home directory).
Working Directory Bug
There is an open issue with WSL2 where sometimes the current working directory cannot be accessed.
You can identify this situation if you ever see the following errors when running a git, docker-compose, or surf command:
fatal: Unable to read current working directory: No such file or directory
or
Traceback (most recent call last): File "docker-compose", line 3, in <module> File "compose/cli/main.py", line 81, in main File "compose/cli/main.py", line 200, in perform_command File "compose/cli/command.py", line 70, in project_from_options File "compose/cli/command.py", line 144, in get_project File "compose/config/config.py", line 317, in find File "compose/config/config.py", line 353, in get_default_config_files File "compose/config/config.py", line 389, in find_candidates_in_parent_dirs File "posixpath.py", line 383, in abspath FileNotFoundError: [Errno 2] No such file or directory [13669] Failed to execute script docker-compose
A workaround for when this issue pops up is to run cd $(pwd).
This can be simplified with the following shell alias:
alias wtf='cd $(pwd)'
Cloud Environments
The LaraSurf CLI provides commands to complete various tasks in cloud environments (AWS).
All commands prefixed with cloud- interact with AWS services.
Some of the commands prefixed with cloud- will result in recurring AWS charges.
Configuration
The larasurf.json file at the root of your project directory contains data representing which environments exist for your project.
If your project has a Stage or Production environment, a starter CircleCI configuration and CloudFormation template have been created for you as part of project generation.
You may change the configured environments after project generation in the following ways:
- From Local to Local and Production
- From Local to Local, Stage, and Production
- From Local and Production to Local, Stage, and Production
No AWS resources will be affected by changing the configured environments for your project.
You must be on the main branch with no uncommitted changes in your working tree to change the configured environments for your project. After changing the configured environments you will be on the develop branch.
After changing the configured environments for your project you should use git to commit the local changes.
Adding Production
To configure your project to have a Production environment in addition to your Local environment, use the configure-new-environments command:
surf configure-new-environments --environments production
Adding Stage and Production
To configure your project to have a Stage environment and a Production environment in addition to your Local environment, use the configure-new-environments command:
surf configure-new-environments --environments stage-production
Adding Stage
To configure your project to have a Stage in addition to your Local environment and Production environment, use the configure-new-environments command:
surf configure-new-environments --environments stage
Domains
Before a project can be deployed to Stage or Production, a Hosted Zone for the domain must exist in AWS.
If you have purchased a domain through the AWS web console, a Hosted Zone for the domain has already been created and there's nothing more you need to do.
Checking if a Hosted Zone Exists
You can determine if a Hosted Zone exists for a specified domain using the following command syntax:
surf cloud-domains hosted-zone-exists --domain <domain>
For example:
surf cloud-domains hosted-zone-exists --domain example.com
Creating a Hosted Zone
You can create a Hosted Zone for a domain using the following command syntax:
surf cloud-domains create-hosted-zone --domain <domain>
For example:
surf cloud-domains create-hosted-zone --domain example.com
The only reason you would need to create a Hosted Zone on AWS yourself is if you purchased your domain elsewhere. If so, you will need to both create the Hosted Zone and point your domain at the supplied nameservers within your domain registrar (see command below).
Listing Nameservers for a Hosted Zone
You can list the nameservers for a Hosted Zone using the following command syntax:
surf cloud-domains nameservers --domain <domain>
For example:
surf cloud-domains nameservers --domain example.com
Users
In order for CircleCI to change AWS resources on your behalf, an IAM User for CircleCI to use must exist.
Creating Users
Creating a cloud user for CircleCI can be done with the following command:
surf cloud-users create --user circleci
The above command creates an IAM User with administrator access for simplicity.
It is recommended to modify this IAM User's attached policies to only include the bare minimum permissions needed to change your infrastructure.
Deleting Users
Deleting a cloud user for CircleCI can be done with the following command:
surf cloud-users delete --user circleci
Images
Before CircleCI can publish container images to AWS ECR as part of the defined pipeline, image repositories for the environment must first be created.
Creating Image Repositories
To create image repositories for the specified environment, use the following syntax:
surf cloud-images create-repos --environment <environment>
For example:
surf cloud-images create-repos --environment stage
surf cloud-images create-repos --environment production
You must be on the develop branch to create image repositories.
After the repositories are created for the specified environment, you should commit the changes that have been made to your local larasurf.json file (and push to GitHub).
If you do not wish to wait for CircleCI to run the defined pipeline for your commit to develop, you can prepend your commit message with [skip ci].
Deleting Image Repositories
To delete image repositories for the specified environment use the following command syntax:
surf cloud-images delete-repos --environment <environment>
For example:
surf cloud-images delete-repos --environment stage
surf cloud-images delete-repos --environment production
If the infrastructure stack for an environment exists, that must first be deleted before image repositories can be deleted.
Building Images
Container images for both the application and webserver of a LaraSurf project are built automatically via the defined CircleCI pipeline. All that's needed is a commit to the stage or main branch.
Following the branching pattern described here, after creating image repositories the next step is to merge develop into stage (and possibly stage into main) to trigger CircleCI.
For example, if you've just created image repositories for the Stage environment and you've committed the larasurf.json changes to develop, the following sequence of commands cloud be executed to merge develop into stage and create the stage branch on GitHub.
git checkout stage
git merge develop
git push -u origin stage
Infrastructure Stacks
An infrastructure stack (CloudFormation stack) is a collection of resources grouped together and created or updated using a template.
Creating Stacks
If you have just triggered CircleCI to build images for an environment's branch (stage or main), the next step is to create the infrastructure stack for the same environment.
This can be done using the following command syntax:
surf cloud-stacks create --environment <environment>
For example:
surf cloud-stacks create --environment stage
surf cloud-stacks create --environment production
This command will prompt you for various inputs to gather desired configuration for your environment.
These inputs include:
- The database instance type
- The database storage to allocate (GB)
- The cache node type
- The CPU for the task definition
- The memory for the task definition
- The minimum number of task containers for autoscaling
- The maximum number of task containers for autoscaling
- The target CPU value for autoscaling
- The scale-out cooldown period (seconds)
- The scale-in cooldown period (seconds)
- The number of queue worker task containers
- The fully qualified domain name for the environment
- The ACM certificate ARN to use, if it already exists
- If an ACM certificate for your fully qualified domain is not specified, one will be created and verified on your behalf
Please be prepared to wait up to 60 minutes for your infrastructure stack to finish creating.
LaraSurf will need to first create the stack, then create cloud variables (Parameter Store secrets) for your application, then update the recently created stack with these variables.
Updating Stacks
If you have created new cloud variables (more on this below), have modified your project's CloudFormation template, or want to change one of the configuration options entered during stack creation, you can use the following command syntax:
surf cloud-stacks update --environment <environment>
For example:
surf cloud-stacks update --environment stage
surf cloud-stacks update --environment production
This command will prompt you for which configuration(s) you'd like to change, or you can select none of them to simply update the stack with the most recent cloud variables and infrastructure template (specific to your local working copy of the code).
It is recommended to make infrastructure changes using the above command instead of letting CircleCI do it as part of the pipeline for the stage or production branch.
Deleting Stacks
The first step of tearing down an evironment is deleting the previously created stack.
This can be done using the following command command syntax:
surf cloud-stacks delete --environment <environment>
For example:
surf cloud-stacks delete --environment stage
surf cloud-stacks delete --environment production
Some resources will not be deleted as part of stack deletion, such as the S3 bucket, parameters in SSM Parameter Store, and the ACM certificate. These resources will need to be deleted manually through the AWS web console (if desired).
Environment Variables
Environment variables for your application in cloud environments are stored in AWS SSM Parameter Store as secure strings.
As part of the deployment process, LaraSurf will gather all environment variables from Parameter Store and set them to be loaded as environment variables within the application container.
This means that if environment variables are changed, the container must be updated before changes will be reflected in the environment (which can be done with the cloud-stacks update command).
Listing Environment Variables
Environment variables defined for the Stage or Production environment can be listed using the following command syntax:
surf cloud-vars list --environment <environment>
For example:
surf cloud-vars list --environment stage
surf cloud-vars list --environment production
Listing with Values
By default, the cloud-vars list command will not display the values of each variable.
To do so, you may add the --values option to the above command like so:
surf cloud-vars list --values --environment <environment>
For example:
surf cloud-vars list --values --environment stage
surf cloud-vars list --values --environment production
Getting an Environment Variable
To get the value of an individual environment variable, the following command syntax can be used:
surf cloud-vars get --environment <environment> --key <key>
For example:
surf cloud-vars get --environment stage --key APP_KEY
surf cloud-vars get --environment production --key APP_KEY
Setting an Environment Variable
To set the value of an environment variable (new or overwrite existing), the following command syntax can be used:
surf cloud-vars put --environment <environment> --key <key> --value <value>
For example:
surf cloud-vars set --environment stage --key SOME_API_KEY --value a1b2c3d4
surf cloud-vars set --environment production --key SOME_API_KEY --value a1b2c3d4
Deleting an Environment Variable
To delete an environment variable, the following command syntax can be used:
surf cloud-vars delete --environment <environment> --key <key>
For example:
surf cloud-vars delete --environment stage --key SOME_API_KEY
surf cloud-vars delete --environment production --key SOME_API_KEY
Ingress
Ingress to both the application and the database can be managed using the cloud-ingress command.
Ingress to the application means the specified source can interact with the application over HTTPS.
Ingress to the database means the specified source can connect to the database, but only if the correct credentials are given.
The --type given to the cloud-ingress command can be application or database.
The --source given to the cloud-ingress command can be me (for your IP address), public (for any IP address) or an IPv4 address.
Allowing Ingress for a Source
To allow ingress for a source into either the application or the database, the following command syntax can be used:
surf cloud-ingress allow --environment <environment> --type <type> --source <source>
For example:
surf cloud-ingress allow --environment stage --type application --source me
surf cloud-ingress allow --environment stage --type application --source public
surf cloud-ingress allow --environment stage --type application --source 1.2.3.4
surf cloud-ingress allow --environment stage --type database --source me
surf cloud-ingress allow --environment stage --type database --source 1.2.3.4
surf cloud-ingress allow --environment production --type application --source me
surf cloud-ingress allow --environment production --type application --source public
surf cloud-ingress allow --environment production --type application --source 1.2.3.4
surf cloud-ingress allow --environment production --type database --source me
surf cloud-ingress allow --environment production --type database --source 1.2.3.4
If at least one ingress rule allows access for a source, ingress will be allowed.
A common pattern is to allow ingress into the application from only your IP address, ensure things are working as expected, then allow public ingress into the application, and then revoke ingress from your IP address (ingress from your IP address will still be allowed because of the public rule).
Revoking Ingress for a Source
To revoke ingress for a source into either the application or database, the following command syntax can be used:
surf cloud-ingress revoke --environment <environment> --type <type> --source <source>
For example:
surf cloud-ingress revoke --environment stage --type application --source me
surf cloud-ingress revoke --environment stage --type application --source public
surf cloud-ingress revoke --environment stage --type application --source 1.2.3.4
surf cloud-ingress revoke --environment stage --type database --source me
surf cloud-ingress revoke --environment stage --type database --source 1.2.3.4
surf cloud-ingress revoke --environment production --type application --source me
surf cloud-ingress revoke --environment production --type application --source public
surf cloud-ingress revoke --environment production --type application --source 1.2.3.4
surf cloud-ingress revoke --environment production --type database --source me
surf cloud-ingress revoke --environment production --type database --source 1.2.3.4
Listing Ingress Rules
To check which sources are currently allowed ingress into either the application or database, the following command syntax can be used:
surf cloud-ingress list --environment <environment> --type <type>
For example:
surf cloud-ingress list --environment stage --type application
surf cloud-ingress list --environment stage --type database
surf cloud-ingress list --environment production --type application
surf cloud-ingress list --environment production --type database
Email Sending
If your application send emails, LaraSurf assumes you will be using a fake SMTP server like MailTrap for the Stage environment and will be sending live emails from the Production environment.
By default LaraSurf sets the MAIL_MAILER variable to smtp for Stage and ses for Production.
SMTP
To configure SMTP sending for your application on either environment, simply use the cloud-vars command to set the appropriate environment variables.
For example:
surf cloud-vars put --environment stage --key MAIL_MAILER --value smtp
surf cloud-vars put --environment stage --key MAIL_HOST --value smtp.mailtrap.io
surf cloud-vars put --environment stage --key MAIL_PORT --value 2525
surf cloud-vars put --environment stage --key MAIL_USERNAME --value a1b2c3d4e5
surf cloud-vars put --environment stage --key MAIL_PASSWORD --value 9z8y7x6w5v
surf cloud-vars put --environment stage --key MAIL_ENCRYPTION --value tls
AWS SES
To configure SES ending for your application on either environment, you can use the cloud-vars command to set the appropriate environment variable.
For example:
surf cloud-vars put --environment production --key MAIL_MAILER --value ses
SES Domain Verification
To verify your environment's domain for both SPF and DKIM, use the following command syntax:
surf cloud-emails verify-domain --environment <environment>
For example:
surf cloud-emails verify-domain --environment stage
surf cloud-emails verify-domain --environment production
The verify-domain subcommand must be done after the stack has been created for an environment.
SES Enabling Live Emails
By default, AWS account have SES in sandbox mode to reduce spam. To request that your account be taken out of sandbox mode and allow for live emails to be sent, use the following command and follow the prompts:
surf cloud-emails enable-sending
Please allow up to 24 hours for AWS to respond to your request.
File Storage
Part of the infrastructure stack is an S3 bucket for file storage. Using S3 should almost always be preferred for any dynamically created file because each container within the ECS cluster will have a separate local filesystem.
As part of the cloud-stacks create command, your application will be configured to use the created S3 bucket.
To upload files directly to S3 from the client side, use pre-signed URLs and an XMLHttpRequest.
Queue
Part of the infrastructure stack is an SQS queue for queued jobs.
As part of the cloud-stacks create command, your application will be configured to use created SQS queue.
Scheduled Commands
Part of the infrastructure stack is a CloudWatch Events rule that runs an ECS task responsible for invoking artisan to check for scheduled commands. This occurs every five minutes.
There's nothing additional you need to do to configure your application to support scheduled commands.
Artisan Commands
Artisan commands can be run on demand on a specific cloud environment using the following command syntax:
surf cloud-artisan "artisan command" --environment <environment>
For example:
surf cloud-artisan "migrate:fresh --seed" --environment stage
surf cloud-artisan "db:seed --class MySeeder" --environment production
After the artisan command finishes the output of the command will be displayed in your terminal.
Tinker
Creating a Tinker session for a specific cloud environment is also supported using the following command syntax:
surf cloud-artisan tinker --environment <environment>
For example:
surf cloud-artisan tinker --environment stage
surf cloud-artisan tinker --environment production
Please be patient when running any artisan command on a cloud environment as it can take some time due to the need to run a new task on ECS.
AWS CLI
Direct interaction with cloud infrastructure is supported without needing to natively install the AWS CLI.
AWS CLI v2 commands can be executed using the following command syntax:
surf aws <command>
For example:
surf aws s3 ls
surf aws s3 ls s3://my-bucket
Health Checks
As part of project generation, LaraSurf has automatically added the route /api/healthcheck which returns a 200.
This is used for the Load Balancer's Target Group.
Logs
Container logs for the webserver, application, queue workers, and scheduler are all shipped to CloudWatch.
These logs can be viewed in the AWS web console and are prefixed by project name, environment, and source type.
LaraSurf CLI Reference
CircleCI
Command signature:
surf circleci <subcommand>
Arguments:
- subcommand: the subcommand to run
- set-api-key: set the CircleCI API token
- clear-api-key: clear the stored CircleCI API token
Cloud Artisan
Command signature:
surf cloud-artisan "<command>" --environment <environment>
Arguments:
- command: the command to run, in quotes
Options:
- environment: the cloud environment
- stage
- production
Cloud Domains
Command signature:
surf cloud-domains <subcommand> --domain <domain>
Arguments:
- subcommand: the subcommand to run
- hosted-zone-exists: check if a Hosted Zone exists for the domain
- create-hosted-zone: create a Hosted Zone for the domain
- nameservers: get the nameservers for a Hosted Zone by domain
Options:
- domain: the domain name
Cloud Emails
Command signature:
surf cloud-emails <subcommand> --environment <environment>
Arguments:
- subcommand: the subcommand to run
- verify-domain: verify the environment's domain name for email sending, both SPF and DKIM
- check-verification: check SPF and DKIM verification for the environment's domain name
- enable-sending: request to get out of the AWS SES sandbox
- does not require the environment option
- check-sending: check if live email sending is enabled (not in sandbox)
- does not require the environment option
Options:
- environment: the cloud environment
- stage
- production
Cloud Images
Command signature:
surf cloud-images <subcommand> --environment <environment>
Arguments:
- subcommand: the subcommand to run
- create-repos: create container image repositories on AWS ECR for the specified environment
- delete-repos: delete container image repositories on AWS ECR for the specified environment
Options:
- environment: the cloud environment
- stage
- production
Cloud Ingress
Command signature:
surf cloud-ingress <subcommand> --environment <environment> --type <type> --source <source>
Arguments:
- subcommand: the subcommand to run
- allow: allow ingress of the specified type from the specified source for the specified environment
- revoke: revoke ingress of the specified type from the specified source for the specified environment
- list: list ingress rules of the specified type for the specified environment
- does not require the type or source options
Options:
- environment: the cloud environment
- stage
- production
- type: the ingress type
- application
- database
- source: the ingress source
- me
- public
- any IPv4 address
Cloud Stacks
Command signature:
surf cloud-stacks <subcommand> --environment <environment> --key <key>
Arguments:
- subcommand: the subcommand to run
- status: get the status of the stack for the specified environment
- does not require the key option
- create: create the stack for the specified environment
- does not require the key option
- update: update the stack for the specified environment
- does not require the key option
- delete: delete the stack for the specified environment
- does not require the key option
- wait: wait for the current stack changes for the specified environment
- does not require the key option
- output: get the specified stack output for the specified environment
Options:
- environment: the cloud environment
- stage
- production
- key: the stack output key
Cloud Users
Command signature:
surf cloud-users <subcommand> --user <user>
Arguments:
- subcommand: the subcommand to run
- create: create the specified cloud user
- delete: delete the specified cloud user
Options:
- user: the user to create or delete
- circleci
Cloud Vars
Command signature:
surf cloud-vars <subcommand> --environment <environment> --key <key> --value <value> --values
Arguments:
- subcommand: the subcommand to run
- exists: determines if a cloud variable under the specified key exists for the specified environment
- does not require the value or values options
- get: gets the value of the cloud variable under the specified key for the specified environment
- does not require the value or values options
- put: sets or overwrites the value of a cloud variable under the specified key with the specified value for the specified environment
- does not require the values option
- delete: deletes the cloud variable under the specified key for the specified environment
- does not require the value or values options
- list: lists the existing cloud variables for the specified environment
- does not require the key or value options
- the values option is optional
Options:
- environment: the cloud environment
- stage
- production
- key: the cloud variable key
- value: the cloud variable value
- values: specifies the listed cloud variables should display decrypted values
Config
Command signature:
surf config <subcommand> <key> <value?>
Arguments:
- subcommand: the subcommand to run
- get: get the value under the specified key
- set: set the value under the specified key to the specified value
- key: the configuration key
- supports dot notation
- value: the value to set
- only required for the set subcommand
Configure New Environments
Command signature:
surf configure-new-environments --environments <environments>
Options:
- environments: the new environment(s) to configure
- production
- stage-production
- stage
TL;DR Checklist
The following is a TL;DR checklist for:
- generating a new project
- deploying to a stage environment
- deploying to a production environment
We strongly encourage you to read the documentation in its entirety before getting started.
This checklist assumes you have completed all prerequisites so be sure you have read and done everything required as described in the Prerequisites section.
New Project Setup
if [ -n "$ZSH_VERSION" ]; then echo "alias surf='vendor/bin/surf'" >> ~/.zshenv && source ~/.zshenv; elif [ -f ~/.bash_profile ]; then echo "alias surf='vendor/bin/surf'" >> ~/.bash_profile; else echo "if [ -f ~/.bashrc ]; then source ~/.bashrc; fi" >> ~/.bash_profile && echo "alias surf='vendor/bin/surf'" >> ~/.bash_profile && source ~/.bash_profile; fi
git remote add origin git@github.com:my-org/my-project.git
git push -u origin develop
surf circleci set-api-key
surf aws configure
surf cloud-users create --user circleci
Deploy to Stage
surf cloud-images create-repos --environment stage
git add .
git commit -m "[skip ci] larasurf init stage"
git push
git checkout stage
git merge develop
git push -u origin stage
surf cloud-stacks create --environment stage
surf cloud-vars put --environment stage --key MAIL_HOST --value smtp.mailtrap.io
surf cloud-vars put --environment stage --key MAIL_PORT --value 2525
surf cloud-vars put --environment stage --key MAIL_USERNAME --value <your SMTP username>
surf cloud-vars put --environment stage --key MAIL_PASSWORD --value <your SMTP password>
surf cloud-vars put --environment stage --key MAIL_ENCRYPTION --value tls
Deploy to Production
git checkout develop
surf cloud-images create-repos --environment production
git add .
git commit -m "[skip ci] larasurf init production"
git push
git checkout stage
git merge develop
git push
git checkout main
git merge stage
git push -u origin main
surf cloud-stacks create --environment production
surf cloud-emails verify domain --environment production
surf cloud-emails enable-sending
surf cloud-ingress allow --environment production --type application --source public