A brief description about what this project is and how it is used.
For each service you integrate with (for example, Stripe) create a section here.
Explain what the service is used for.
Document how to create a test account for the service or how to gain access to a pre-existing test account for this project. Explain where production credentials for this service can be obtained.
Document exactly which .env
keys need to be set. If any manual setup within the account is necessary (for example,
specific products or webhooks for Stripe), document it step-by-step.
To get started with local development, follow these steps. Make sure you run all commands from the top-level of your project.
Decide on a local domain to use for this project. Substitute that any place you see
my-project.local
ormy-project
below.
By default, this project will run on the host my-project.local
.
OrbStack (see below) is already configured to serve on this top-level domain.
If you're not using OrbStack, you will need some sort of local DNS resolution for that hostname to your localhost IP address.
One easy way to do this for the entire .local
top-level domain, is to run a lightweight tool called dnsmasq
.
You can install it via Homebrew on a Mac with: brew install dnsmasq
.
If you've ever setup Valet, it already installed dnsmasq for you. You can verify if it's already installed by running
brew services
and see ifdnsmasq
is listed.
If you don't want to run dnsmasq
, you can also add a manual DNS entry to your /etc/hosts
file in the form:
127.0.0.1 my-project.local
Orbstack provides automatic TLS certificates for .local
domains, but we will also need to create one that Vite can use.
To do this, we'll use mkcert
, a simple tool for making locally-trusted development certificates.
To install on Mac with Homebrew, you can run:
brew install mkcert nss
For other platforms or configurations, you can refer to the mkcert README
Once mkcert
is installed, we need to generate our local development root certificate authority:
If you've used
mkcert
before, you can skip this step This command will likely prompt you for administrator access on macOS
mkcert -install
Then, generate the certificates for this project and put them into a location accessible to your docker setup:
mkcert -cert-file docker/vite/ssl.pem -key-file docker/vite/key.pem my-project.test
We're using the .test
top-level domain for the Vite server.
The best option to ensure you're using the correct versions of Node and npm with this project is to install Volta.
Volta will read the pinned versions of Node and npm from the package.json
so you can be sure you're using the correct versions.
Docker is used for local development. It's self-contained, easy to set up, and matches the exact versions of key services running in production. We use OrbStack as our Docker engine, since it handles domain resolution and port mapping. This config also works with Docker Desktop, but you may need to add explicit port mappings.
Setup the environment
Open the .env.example
file and update the COMPOSE_PROJECT_NAME
and DOCKER_SERVER_NAME
settings to match your project.
Make a copy of the example env file: cp .env.example .env
Get Docker running
Most of our Docker configuration is managed with Docker Compose. You can view the docker-compose.yml
file to see how it
is configured.
To build and bring up the Docker containers, run docker compose up -d
. If this is your first time running the command, it
may take a few minutes to pull down images and build the containers. Subsequent runs will be much quicker.
You can verify that your containers are in a running state with docker compose ps
.
Composer licensing
If this project uses a paid Composer package, like Nova, document it here
This project uses a paid Composer package package-name
, and in order to do a composer install
, you need a valid license key.
To set that up, create an auth.json
file from the example:
cp auth.json.example auth.json
And then open auth.json
and fill out the username and password values.
Normal project setup
With the certificates, our environment, and Docker setup, the rest of these steps will be typical steps for any Laravel project. The one key difference is that instead of running tools like composer and artisan directly, we need to run them from inside the container. This is very important. If we run the tools from our host environment, all the guarantees about versions of tooling will no longer apply.
To make it easier to run tools via Docker, a collection of simple shell scripts exists in the project's docker/bin
directory.
Run these commands to finish the local development setup
npm install
npm run dev
docker/bin/composer install
docker/bin/composer run post-root-package-install
docker/bin/composer run post-create-project-cmd
docker/bin/artisan horizon:install
docker/bin/artisan migrate --seed
You're good to go - surf to https://my-project.local
You can also use any normal database management tools and connect to the database using the port specified in .env
.
Generally speaking, you don't want your Docker containers running unless you're actively using them. So each time you're ready to start development, you would start your Docker environment:
docker compose up -d
The
-d
means "detach" and it runs the containers in the background, so you can continue to use your terminal. If you run without-d
, the terminal would remain bound to the containers, and you'd see a stream of container log output in your terminal. This can be useful when debugging a Docker issue, but for normal development, running in the background is best.
Then, when you're done for the day, you can stop the Docker environment:
docker compose stop
There is also a
down
command, and it might seem more logical as the opposite action ofup
. This command not only stops the containers, but removes them along with the Docker network. This doesn't harm anything, and no data will be lost if you rundown
instead, but there's no need to constantly remove and recreate the containers, sostop
is a better choice.
The .env.example
file sets up queues to run with the redis
configuration, and we use Horizon to manage the queue workers.
By default docker compose up
will start a Horizon worker in a separate container. If, for some reason you want to stop
Horizon, you can stop that container with docker compose stop horizon
.
Be aware, if you're changing code and also interacting with the queues, you will need to restart horizon in order to have the new code loaded:
docker compose restart horizon
One of the goals of Docker is to match your staging/production environments precisely. If you are planning an upgrade of PHP, nginx, or MySQL in those environments, you should first upgrade your Docker environment, and validate everything works as expected.
This section contains some guidelines on how to upgrade your Docker configuration.
If you use Postgres instead, this is basically the same. Substitute in postgres for mysql as needed. See the
docker-compose.yml
file for a commented-out Postgres configuration you could use.
In the docker-compose.yml
file, update the image property for both the mysql
and mysql-test
services. You can
find a specific image version on Docker Hub. Make sure to stick with the alpine images. These
will all have the -alpine
suffix.
If you want to change any of the parameters about MySQL, like the user and password, you'll also need to delete the volumes before bringing up the new containers:
docker volume rm my-project_mysql-data my-project_mysql-test-data
If you don't, db initialization will be skipped, and it will still be running with the old parameters.
After updating the file, follow these steps:
- Bring down the current containers:
docker compose stop mysql mysql-test
- Remove the containers:
docker compose rm
(It should ask if you want to remove the two containers you just stopped) - Bring up the new containers:
docker compose up -d
. You should see it pull the new images before starting.
To verify the correct version is now running, you can open the container in Docker Desktop, and you should see the new MySQL version mentioned in the log output.
If you want to change the version of nginx, edit the first line in docker/nginx/Dockerfile
to reflect the desired version
number. Make sure to pick the alpine version of any image.
If you want to make a configuration change, you can do this in docker/nginx/nginx.conf
.
For either type of change, you'll need to rebuild the images:
- Bring down the current containers:
docker compose stop nginx
- Remove the containers:
docker compose rm
(It should ask if you want to remove the container you just stopped) - Rebuild the containers:
docker compose build --no-cache nginx
(Bypassing the cache isn't always necessary, but it's a safe default) - Bring up the new containers:
docker compose up -d
. You should see it pull the new images before starting.
To verify the correct version is now running, you can open the container in Docker Desktop, and you should see the new
nginx version mentioned in the log output. If it doesn't show in the logs, you can click the CLI button for the container
and run nginx -v
from the terminal that launches.
If you want to change the version of PHP, edit the first line in docker/php-fpm/Dockerfile
to reflect the desired version
number. Make sure to pick the alpine version of any image, and make sure it's the fpm
image or else nginx won't work with it.
If you want to add or change any extensions, you can do that by modifying the RUN docker-php-ext-install
command. Some
extensions will also require OS dependencies, which you can add in the RUN apk add
command.
If you want to update Composer, change the version of composer in the first COPY
command.
You can also modify any .ini
files or add entirely new ones by creating the file and adding a corresponding COPY
command.
For all types of change, you'll need to rebuild the images:
- Bring down the current containers:
docker compose stop php-fpm php-fpm-debug
- Remove the containers:
docker compose rm
(It should ask if you want to remove the two containers you just stopped) - Rebuild the containers:
docker compose build --no-cache php-fpm php-fpm-debug
(Bypassing the cache isn't always necessary, but it's a safe default) - Bring up the new containers:
docker compose up -d
. You should see it pull the new images before starting.
To verify the correct version is now running, you can open the container in Docker Desktop, click the CLI button for the
container and run php -v
from the terminal that launches.
If you want to change the version of node
, run volta pin node@XYZ
where XYZ
is the desired version. The same works for npm
with volta pin npm@XYZ
This will update the corresponding sections of the volta
object in package.json
.
To verify the correct version is now running, you can run node -v
or npm -v
.
Continuous Integration is run on every branch, using Github Actions. The workflow exists at .github/workflows/ci.yml
List each environment in use, like staging and production. For each environment, document where it is hosted and the basic infrastructure in use.
Document how new code makes it into each environment. Does a particular branch correspond to an environment? If the deployment process is automated, document what the triggers are. If it's a manual process, provide step-by-step instructions on how to deploy.
If this project produces code artifacts to be used by someone else (for example, a Vue component), document that here and provide detailed instructions on how to use the artifact. These should have enough context, that they could be copied and pasted into an email to some other dev not on this project, and they would understand how to make use of it.
Ideally all typical developer tasks would be automated, but sometimes a manual process is necessary. If so, describe it here in precise detail: what is the process, when would you need to do it, what are the exact steps?
If there are any relevant bits of architectural history, include them here. For example, is this a major rewrite of some previous codebase? Mention it here. Include anything that you would want to know if you were inheriting the project.
If you'd like, include a link to the laravel-project-standard repo and what version this project is based on.