Laravel on any Developer Machine with Gitpod

Published on by

Laravel on any Developer Machine with Gitpod image

If you've been a developer long enough, you've encountered the excitement of cloning a new repository only to be quickly frustrated by not being able to get the code to compile or run on your machine. For the longest time I've solved this challenge by using Docker and more specifically Docker Compose to provide a consistent way to build and test code locally. While not perfect, it is an improvement but is still lacking in so many ways. But what if there was a way to have a consistent development environment that also allows for standardization and automation. Enter Gitpod, a Cloud Development Environment built to solve just these problems.

As a PHP and Laravel developer, I often struggle with server versions, consistent file packaging, database migrations, and running resources locally to be able to accurately code and test my changes. In this article, I'm going to walk through a basic setup of how to turn a Laravel repository into a Gitpod Project. From that Project, I'll highlight some features of using Gitpod's Flex to walkthrough local code changes and how any new developer can clone my repository and have a fully working local environment through the power of the Gitpod platform.

Disclosure

But before we begin and for disclosure, Gitpod sponsored me to experiment with their product and report my findings. They have rented my attention, but not my opinion. Here is my unbiased view of my experience as a developer when setting up a Gitpod Workspace for coding on a PHP and Laravel application

The Gitpod Problem

I've been a developer since the mid-1990s and have experienced all of the problems that Gitpod is solving. Problems like setting up a local development, getting a build, and shipping that build to a server (or cloud). These seem essential but there are other challenges beyond just the single developer experience. This below image is a fantastic visual for the space that the tooling is operating in.

With the tooling and the problems being established, let's jump into some code and see how Gitpod and Laravel play together with a local setup.

Laravel Application

For the balance of the article, I'll be working with a basic Laravel Application that is using a SQLite database as its only external dependency. The full source code can be found in this Github repository

Getting Started

Normally when I kick off a Laravel project, I start with:

laravel new example-app

However, to take advantage of Gitpod, I need to be building in a containerized environment. To solve this, I'm leaning into Sail. Sail gives me a way to build my PHP application for a Docker environment and provides a generated docker-compose.yml that I can customize to my needs.

All of this is in support of building and interacting with my development environment in a DevContainer. DevContainers can be described like this:

A development container (or dev container for short) allows you to use a container as a full-featured development environment. It can be used to run an application, to separate tools, libraries, or runtimes needed for working with a codebase, and to aid in continuous integration and testing. Dev containers can be run locally or remotely, in a private or public cloud, in a variety of supporting tools and editors -- DevContainers Website

If you aren't familiar with DevContainers, here is the website that can get you up to speed on why you should be paying attention and leveraging them.

In the context of Gipod, the DevContainer specification is natively integrated and is an integral part of the developer experience. For my example, here's the .devcontainers/devcontainer.json in my project.

{
"name": "Existing Docker Compose (Extend)",
"dockerComposeFile": [
"../docker-compose.yml"
],
"service": "laravel.test",
"workspaceFolder": "/var/www/html",
"customizations": {
"vscode": {
"extensions": [
],
"settings": {}
}
},
"remoteUser": "sail",
"postCreateCommand": "chown -R 1000:1000 /var/www/html 2>/dev/null || true"
}

With the project setup, and the following customizations made, my simple application looks like the image below the bullets.

  • Added a Todo Model
  • Added a Todo Migration
  • Added a Todo Seeder
  • Customized the CSS and UI of the TodoView
  • Modified the routes to point / at my TodoController which yields a list of Todos

Diving into Gitpod

If you speak to a developer working on any application with more than 2 dependencies, they'll have the same common pains that they will tell you. Dependency management at the OS and library level can be a pain. Keeping an up to database that the application is congruent with takes work. And they often forget to run migrations and miss hours of their life realizing that they needed to run a migration. Or worse yet, they've joined a new team and lose many days to just getting the project configured.

Now take all of that and the work it takes to do things locally, and try and then bring that effort to the DevOps and Platform team. It's almost like starting over and can be a fragile dance of translation between local and cloud. Imagine being able to satisfy both the development team and the platform team. This is the problem and space that Gitpod lives in.

My example below is just going to scratch the surface. It is going to make use of PHP, Laravel, DevContainers, Gitpod's Flex, a local Gitpod Runner, and some basic Gitpod Automations. From there, I'll leave you with some next steps that you can take to accelerate your learning.

Gitpod Projects and Environments

Gitpod operates around Projects and Environments. My Project is connected directly to my Github repository that I shared above. In the UI, that is represented in a Projects view.

Once I've established a Project, I can then create an Environment. Environments can have Runners which are where your container is hosted. For me, I'm running in a Local Runner, but this could easily be a Region in AWS where I'd get an EC2 server running in a VPC and Subnet of my choosing.

Local Runner

Once I've established these two basic constructs in Gitpod, I'm ready to launch my local environment. This is where the DevContainer comes in. Gitpod is going to read that specification which points back to a Docker Compose file that is going to launch the container that I can code in.

services:
laravel.test:
build:
context: './.devcontainer'
dockerfile: Dockerfile
args:
WWWGROUP: '1000'
image: 'sail-8.3/app'
extra_hosts:
- 'host.docker.internal:host-gateway'
ports:
- '${APP_PORT:-80}:80'
- '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
environment:
WWWUSER: '1000'
LARAVEL_SAIL: 1
XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
IGNITION_LOCAL_SITES_PATH: '${PWD}'
volumes:
- '.:/var/www/html'
networks:
- sail
depends_on: { }
networks:
sail:
driver: bridge

When I click launch, and things go my way, I get the following image with all of these green circles. Red circles would be bad, and I'd have logs and insight into what might have gone wrong.

Automations

At this point, you might be wondering what those Services and Tasks are in the mid to bottom part of the image. Gitpod goes beyond just DevContainers and gives you the ability to customize the way things launch. Automations are powerful and more can be read here.

Essentially, think of them like this. I get points in the launch where I can run commands against my container. For instance, maybe I want to run a DB Migration. Or perhaps I want to compose some dependencies for my Laravel application? These would be called Tasks.

But what about long running things like the PHP application itself? Those would be done via Services. And all of this automation happens by including a .gitpod/automations.yaml file in my project. In my case, it looks like the below. I opted for granularity vs doing them all in one Task just to visualize things better.

services:
php:
name: Run PHP Serve
triggeredBy: ["postDevcontainerStart"]
commands:
start: php artisan serve
 
tasks:
copyEnv:
name: Copy Environment
description: Creates the .env file
triggeredBy:
- postEnvironmentStart
command: cp .env.example .env
appUrl:
name: Mod App URL
dependsOn: ["copyEnv"]
triggeredBy:
- postEnvironmentStart
command: sed -i "s#APP_URL=http://localhost#APP_URL=$(gp url 8000)#g" .env
viteUrl:
name: Mod Vite URL
dependsOn: ["copyEnv"]
triggeredBy:
- postEnvironmentStart
command: sed -i "s#GITPOD_VITE_URL=#GITPOD_VITE_URL=$(gp url 5173)#g" .env
composer:
name: Install Dependencies
dependsOn: ["copyEnv"]
triggeredBy:
- postEnvironmentStart
description: Installs deps via composer
command: composer install --ignore-platform-reqs
createDatabase:
name: Create Database
triggeredBy:
- postEnvironmentStart
dependsOn: ["copyEnv"]
command: touch database/database.sqlite
phpOperations:
name: Setup PHP and Run
triggeredBy:
- postEnvironmentStart
dependsOn: ["copyEnv"]
command: |
php artisan key:generate
php artisan storage:link
php artisan migrate --seed
php artisan db:seed --class=TodoSeeder

Amazing right?!? I can have these steps run when I launch my development environment. And any developer who has access to this project gets the same. And let's say that I need to change steps or workflow, the next time I launch. Those just get run. So much better than a README.md file that might be out of date.

Connecting to My Editor

When using the Gitpod Client, I can then connect my editor or a Terminal to my container. For this, I opted for VSCode because of its popularity. However, there is an integration for Jetbrains IDEs and I also can connect my favorite editor in Neovim via custom Dotfiles. The terminal developer in me is encouraged by this support and I plan to do some deeper dives in this area as I get time.

VSCode has two nice extensions for working with Gitpod and DevContainers. I found that once I launched my Gitpod Environment in VSCode, it was just like working with normal local development. Changes are available immediately and all of the normal Git operations perform just as I expected. And again, all of this is happening in a Container, so no worrying about local dependencies or "it works on my machine".

The last piece of the puzzle is to make sure that I've got ports exposed to my container so that I can serve the traffic from the running Gitpod Service which is just php artisan serve. Once everything is in place, I'm able to serve the traffic in my browser that shows a PHP Laravel Application, backed by SQLite that was migrated and seeded as my Task automations specified.

And the great thing is, I can share this with any teammate, and in the time it takes to pull the Gitpod and DevContainer resources, initialize, and launch, they'll have the same experience. There are just so many problems solved.

Impressions and Thoughts

I hadn't spent a ton of time with CDEs in the past but I'm very impressed with what Gitpod is putting together. I think that the power lies in its repeatability and the ability to automate so many routine tasks in a consistent way that is not runtime specific is just magic.

I didn't get a chance in this article to explore running a Project in the Cloud, but from my exploration of the documentation, I can connect this same project to AWS via a generated CloudFormation template. And since Gitpod comes with a CLI, that same work I do locally, could be connected to an automated pipeline as part of a CI/CD process that can accelerate shipping value to customers.

I did have a couple of bumps along the way though. Gitpod just recently rolled out their Flex product which is what this article is based upon.

First, the documentation is good, but the tutorials left a little to be desired so I did have some figuring out of specific patterns and how the Client UI worked. Hopefully this article and the accompanying code saves you from some of those.

Second, the UI is very good for an early release but there are still some options lacking like refreshing my Git repository if I changed something fundamental. And I found that when dropping to the new CLI, I wasn't able to perform things that I thought I could from the documentation.

The bumps however are totally offset by the fact that the software works amazingly. And pair that with an active development cycle by Gitpod's Github repositories and things will only get better as Flex continues to take off. I am seriously impressed by the workflow and the time savings that this could provide a larger software development team.

Wrapping Up

My goal with this piece was to provide a basic introduction to running a Laravel Application developed in Gitpod with DevContainers and the Flex UI Client. There's so much more to the ecosystem that is worth exploring. You could dive more into Gitpod, the DevContainers specification, or improve the base Dockerfiles that I'm working with in this project.

What I believe is powerful about the CDE approach is that as a developer, you could better partner with your Platform teams to share some of the burden of deployment and provisioning by using the abstractions that Gitpod provides. You also gain a high level of isolation and security by taking this approach. Environments can be isolated to workstations or servers. You can also make use of tenant level configurations. And lastly, every operation that you execute is vetted by credentials and authorizations within the Gitpod API.

Development is moving fast. This approach gives you the control and comfort that the foundations underneath your day-to-day are controlled, managed, and protected.

Thanks for reading and happy building!

Benjamen Pyle photo

Benjamen Pyle is a technology executive and software developer with over 20 years of experience across startups and large enterprises. He is Co-Founder and CEO of Pyle Cloud Technologies.

Filed in:
Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

Laravel Forge logo

Laravel Forge

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Forge
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
No Compromises logo

No Compromises

Joel and Aaron, the two seasoned devs from the No Compromises podcast, are now available to hire for your Laravel project. ⬧ Flat rate of $7500/mo. ⬧ No lengthy sales process. ⬧ No contracts. ⬧ 100% money back guarantee.

No Compromises
Laravel Idea for PhpStorm logo

Laravel Idea for PhpStorm

Ultimate PhpStorm plugin for Laravel developers, delivering lightning-fast code completion, intelligent navigation, and powerful generation tools to supercharge productivity.

Laravel Idea for PhpStorm
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Bacancy logo

Bacancy

Supercharge your project with a seasoned Laravel developer with 4-6 years of experience for just $2500/month. Get 160 hours of dedicated expertise & a risk-free 15-day trial. Schedule a call now!

Bacancy
Lucky Media logo

Lucky Media

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel E-Commerce

E-Commerce for Laravel. An open-source package that brings the power of modern headless e-commerce functionality to Laravel.

Lunar: Laravel E-Commerce
LaraJobs logo

LaraJobs

The official Laravel job board

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit
Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate logo

Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate

Build your SaaS application in hours. Out-of-the-box multi-tenancy and seamless Stripe integration. Supports subscriptions and one-time purchases, allowing you to focus on building and creating without repetitive setup tasks.

Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate
JetShip - Laravel Starter Kit logo

JetShip - Laravel Starter Kit

A Laravel SaaS Boilerplate and a starter kit built on the TALL stack. It includes authentication, payments, admin panels, and more. Launch scalable apps fast with clean code, seamless deployment, and custom branding.

JetShip - Laravel Starter Kit
Rector logo

Rector

Your partner for seamless Laravel upgrades, cutting costs, and accelerating innovation for successful companies

Rector
MongoDB logo

MongoDB

Enhance your PHP applications with the powerful integration of MongoDB and Laravel, empowering developers to build applications with ease and efficiency. Support transactional, search, analytics and mobile use cases while using the familiar Eloquent APIs. Discover how MongoDB's flexible, modern database can transform your Laravel applications.

MongoDB

The latest

View all →
Streamlining Route Parameters in Laravel Using URL Defaults image

Streamlining Route Parameters in Laravel Using URL Defaults

Read article
Flexible Docker Images with PHP INI Environment Variables image

Flexible Docker Images with PHP INI Environment Variables

Read article
Dynamic Form Validation in Laravel with prohibited_if image

Dynamic Form Validation in Laravel with prohibited_if

Read article
Add Approvals to Your Laravel Application image

Add Approvals to Your Laravel Application

Read article
Enhancing Data Processing with Laravel's transform() Method image

Enhancing Data Processing with Laravel's transform() Method

Read article
Get Xdebug Working With Docker and PHP 8.4 in One Minute image

Get Xdebug Working With Docker and PHP 8.4 in One Minute

Read article