Skip to content

Building Docker Containers

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

Revisiting The Basics

In my earlier post, Getting Started with Docker, I covered building a basic Dockerfile using the FROM, COPY, RUN, and CMD instructions and how to use a .dockerignore file to keep unnecessary files out of your images and containers. If you haven't read that post, go check it out to learn the basics of building Docker images. In this post, I'll cover some more advanced techniques for building container images. In addition, I recently published a post exploring advanced Docker CLI usage. I recommend giving it a read, too, if you aren't already a CLI pro.

Installing Dependencies

Using FROM with an official image for your language or framework will get you a long way, but many applications will require a system dependency that's not included in the FROM image. For example, many applications use ImageMagick for processing image uploads, but it's not included by default in the Debian images that most language images are based on. You can use RUN and apt-get to install missing dependencies.

FROM node:15

RUN apt-get update
RUN apt-get install -y imagemagick

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

# Use the start script defined in package.json to start the application
CMD ["npm", "start"]

We started the Dockerfile just like the example from my earlier post, using the official NodeJS 15 image, but then we do 2 additional steps to install ImageMagick using apt-get. To keep the base image size low, Debian does not come pre-loaded with all of the data it needs to install packages from apt-get, so we need to run apt-get update first so that apt-get has that info. Then, we simply use apt-get install -y imagemagick to install imagemagick. The -y option is used to automatically respond with "yes" when apt-get prompts you to confirm the package installation.

RUN vs CMD (vs ENTRYPOINT)

By now you've probably noticed that there are two different instructions that run commands in your containers, RUN and CMD. While both are used to run commands, they're used in very different contexts. As we've seen in previous examples, RUN is used exclusively in the build process to run commands to modify the image as needed. CMD is different because it specifies the command that will be run by the container when you launch it using docker run. You can have as many RUN instructions as you need, but only one CMD. If you need to run a different command at runtime, you can pass it as an argument when you launch the container with docker run (check out my Docker CLI Deep Dive post). Additionally, Docker provides the ENTRYPOINT instruction. This is a command that the command you provide to the CMD instruction will be passed to as arguments. If you do not provide an ENTRYPOINT it will default to /bin/sh -c which will cause your CMD command to execute in a basic unix shell environment. The default ENTRYPOINT will satisfy most use cases. It's possible to override a container's CMD at runtime, but it is not possible to change its ENTRYPOINT. Docker's own ENTRYPOINT documentation goes into more detail about how it can be used.

In the example Dockerfile above, you probably noticed that the way commands are passed to CMD and RUN looks different. Typically, when using RUN you provide commands using shell syntax, and you provide commands to CMD (and ENTRYPOINT) using the exec syntax, but they can be used interchangably. When using shell syntax, you can resolve shell expressions within your command. You can use shell variables and operators like output pipes (|) and redirects (>, >>), as well as boolean operations (&&, ||) to join commands. Exec syntax is much more straightforward. Each string within the bracketed array is joined with the other elements with a space in between and run exactly as provided.

Layers and Caching

Each isntruction in your Dockerfile adds a new Layer to your image. For performance reasons, it's considered a best practice to limit the total number of layers that comprises your finished image. There are a number of ways to do this. The simplest is by combining lines where RUN or COPY are used in close proximity to each other. Consider the example above where we installed ImageMagick; instead of using two separate RUN instructions, we can combine them using the bash && operator.

FROM node:15

RUN apt-get update && apt-get install -y imagemagick

Combining copy commands is a bit easier. The COPY instruction takes any number of arguments. The first N parameters provided to COPY are interpreted as a list of files to copy, and the N+1th paramter is the location to copy those files to. You can also use * as a wildcard character as I did in the first example when copying the package.json and package-lock.json files to the image.

Anothing thing to consider when thinking about how your image layers are composed is caching. When Docker processes your Dockerfile to build your image, it runs each of the instructions in order to create the layers of your image. Docker analyzes each instruction before it is run and checks its cache to determine whether or not there is an identical existing image layer. When analyzing RUN instructions, Docker looks for any cached image layer that was built using the exact same command and uses it instead of rebuilding the same layer. For COPY and ADD instructions, it analyzes the files to be copied and looks for a previously built layer that has the exact same file contents. If at any point any instruction requires its layer to be rebuilt, all of the following instructions will result in a rebuild. Optimizing your Dockerfile to take advantage of the layer cache can greatly reduce the time it takes to build your image. Organize your Dockerfile so that the layers least likely to change are processed first (ex: installing dependencies) and those more likely to change (ex: copying application code) are processed later.

Conclusion

These techniqes will help you create more advanced container images and hopefully help you optimize them. However, I've only covered a small slice of the options available to you when building container images. If you dig deeper into the official Dockerfile reference you'll find information about all of the instructions available to you and more advanced concepts and use cases.

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

You might also like

Getting Started with Docker cover image

Getting Started with Docker

Getting Started With Docker Introduction Docker is quickly becoming one of the most popular technologies for hosting web applications. It is a set of tools for packaging, distributing, and running software applications. Developers can write configuration files to create packages called images, which are distributed via decentralized, web-based repositories (some public, some private). Images downloaded from repositories are used as templates to create isolated environments called "containers" that run applications within them. Many containers may exist alongside each other on a single host. Memory and CPU resources are shared between all the containers running on a machine, but each container has its own fully isolated file system and environment. This is convenient for a number of reasons, but most of all, it simplifies the process of installing and running one or more applications on a single host machine. Installing Docker If you are on MacOS or Windows, the best way to install Docker is by installing Docker Desktop. It provides a complete installation of Docker and provides a GUI for managing it. You can use the GUI to start or stop your Docker daemon, or to manage installing software updates to the Docker platform. (Bonus: Docker Desktop can also manage a local Kubernetes cluster for you. It's not relevant to this article, but it provides a straightforward way to get started with Kubernetes, a platform for managing running containers across a scalable number of hosts). Linux users can install docker from their distribution’s package manager, but the Docker Desktop GUI is not included. Installation instructions for the most popular Linux distributions can be found in the Docker documentation. Working With 3rd Party Containers The first thing to try once you've installed Docker on your computer is running containers based on 3rd party images. This exercise is a great way to quickly display the power of Docker. First open your favorite system terminal and enter docker pull nginx. This command will download the official nginx image from Docker Hub. Docker Hub is a managed host for Docker images. You can think of it sort of like npm for Docker. We've pulled the newest version of the nginx image, however, as with npm, we could have chosen a specific version to download by changing the command to docker pull nginx:1.18. You can find more details about an image, including which versions are available for download, on its Docker Hub page. Now that we've downloaded an image, we can use it to create a container on our local machine just as simply as we downloaded it. Run docker run -d -p 8080:80 nginx to start an nginx container. I’ve added a couple options to the command. By default, nginx runs on port 80, and your system configuration likely prevents you from exposing port 80. Therefore, we use -p 8080:80 to bind port 80 on the container to port 8080 on your local machine. We use -d to detach the running container from the terminal session. This will allow us to continue using the same terminal while the nginx container continues to run in the background. Now, you can navigate to http://localhost:8080 with your web browser, and see the nginx welcome page that is being served from within Docker. You can stop the nginx container running in the background by using the docker kill command. First, you'll need to use docker ps to get its container ID, then you can run docker kill . Now, if you navigate to http://localhost:8080 again, you will be met with an error, and docker ps will show no containers running. The ability to simply download and run any published image is one of the most powerful features of Docker. Docker Hub hosts millions of already baked images, many of which are officially supported by the developer of the software contained within. This allows you to quickly and easily deploy 3rd party software to your servers and workstations without having to follow bespoke installation processes. However, this isn’t all that Docker can do. You can also use it build your own images so that you can benefit from the same streamlined deployment processes for your own software. Build Your Own As I said before, Docker isn’t only good for running software applications from 3rd parties. You can build and publish your own images, so that your applications can also benefit from the streamlined deployment workflows that Docker provides. Docker images are built using 2 configuration files, Dockerfile and .dockerignore. Dockerfile is the most important of the two. It contains instructions for telling docker how to run your application within a container. The .dockerignore file is similar to Git’s .gitignore file. It contains a list of project files that should never be copied into container images. For this example, we'll Dockerize a dead "hello world" app, written with Node.js and Express. Our example project has a package.json and index.js like the following: package.json: ` --- index.js: ` The package.json manages our single express dependency, and configures an npm start command with which to start the application. In index.js, I've defined a basic express app that responds to requests on the root path with a greeting message. The first step to Dockerizing this application is creating a Dockerfile. The first thing we should do with our empty Dockerfile is add a FROM directive. This tells Docker which image we want to use as the base for our application image. Any Docker image published to a repository can be used in your FROM directive. Since we've created a Node.js application, we'll use the official node docker image. This will prevent us from needing to install Node.js on our own. Add the following to the top of your empty Dockerfile: ` Next, we need to make sure that our npm dependencies are installed into the container so that the application will run. We will use the COPY and RUN directives to copy our package.json file (along with the package-lock.json that was generated when modules were installed locally) and run npm install. We'll also use the WORKDIR directive to create a folder and make it the image's working directory. Add the following to the bottom of your Dockerfile: ` Now that we've configured the image so that Docker installs the application dependencies, we need to copy our application code and tell Docker how to run our application. We will again use COPY, but we’ll add CMD and EXPOSE directives as well. These will explain to Docker how to start our application and which ports it needs exposed to operate. Add these lines to your Dockerfile: ` Your completed Dockerfile should look like this: ` Now that we have a complete Dockerfile, we need to create a .dockerignore as well. Since our project is simple, we only need to ignore our local node_modules folder. That will ensure that the locally installed modules aren’t copied from your local disk via the COPY . . directive in our Dockerfile after they've already been installed into the container image with npm. We'll also ignore npm debug logs since they're never needed, and it's a best practice to keep Docker images' storage footprints as small as possible. Add the following .dockerignore to the project directory: ` On a larger project, you would want to add things like the .git folder and any text and/or configuration files that aren't required for the app to run, like continuous integration configuration, or project readme files. Now that we've got our Docker configuration files, we can build an image and run it! In order to build your Docker image open your terminal and navigate to the same location where your Dockerfile is, then run docker build -t hello-world .. Docker will look for your Dockerfile in the working folder, and will build an image, giving it a tag of “hello-world”. The “tag” is just a name we can use later to reference the image. Once your image build has completed, you can run it! Just as you did before with nginx, simply run docker run -d -p 3000:3000 hello-world. Now, you can navigate your browser to http://localhost:3000, and you will be politely greeted by our example application. You may also use docker ps and docker kill as before in order to verify or stop the running container. Conclusion By now, it should be clear to see the power that Docker provides. Not only does Docker make it incredibly easy to run 3rd party software and applications in your cloud, it also gives you tools for making it just as simple to deploy your own applications. Here, we've only scratched the surface of what Docker is capable of. Stay tuned to the This Dot blog for more information about how you can use Docker and other cloud native technologies with your applications....

Publishing Docker Containers cover image

Publishing Docker Containers

Publishing Docker Containers If you've read my previous blog posts, Getting Started With Docker and Building Docker Containers, you may find yourself wondering what your options are for publishing your custom docker image. Thankfully, publishing your custom images is one of the simplest things you can do. I'll show you how. I'm going to assume you've already got Docker installed locally. If you don't have it and aren't sure how to get it, check out the older posts I linked before. This Is How We Do It The fastest and easiest way to get started publishing images is to make yourself a DockerHub account. Once you've done this, you'll need to log in with your Docker client. At a command prompt, enter the following: ` In return, you'll be prompted for your DockerHub username and password. Enter them to complete the login process. Once you've logged in successfully, you're ready to start publishing images. Publishing a public docker image to your personal account is incredibly easy. First, you need to make sure your image is tagged appropriately. You'll need to prefix the container's name with your DockerHub username so that docker knows what to do. Then you can publish using docker push. The whole process goes like this: ` You will see the status of your image upload. Once it's complete, head over to hub.docker.com and log in to see your published image. It's important to note that following this process will publish your image publicly. Anyone will be able to view your DockerHub profile and download your image. DockerHub does support private repositories, but only provides one free private image per account (paid accounts). You can make the image you just uploaded private by navigating to it from your DockerHub dashboard, selecting the "Settings" tab, and clicking the "Make Private" button. Alternatively, if you'd prefer to make sure your image is private as soon as you publish it, you may create your private repository on DockerHub, _before_ you use docker push to publish it. Click "Create Repository" on the DockerHub dashboard (after logging in) and follow the instructions given. Alternatives To DockerHub DockerHub isn't the only place you can publish your Docker image artifacts online. There are a number of other image repository hosts you can use both managed and self-hosted that offer a similar feature set to that of DockerHub. Amazon, Google, Microsoft each have a container registry offering, so if you're already using one of those clouds for hosting, you can leverage those providers' own solutions to keep your billing consolidated. Alternatively, GitHub and GitLab users can choose to keep their container images in those services alongside their application code. These are just a handful of the options available to you. A quick Google search will reveal even more vendors like Sloppy.io and Quay.io. For some, whether because of personal preference or business requirements, storing images on the public internet won't be desireable. The good news is that there are options for folks who need or want to host their images within their own private networks, or simply want to maintain control of their data. Two of the most popular open-source registry hosts are Harbor and Artifactory. Harbor is a Kubernetes (Cloud Native) focused solution. It also acts as a repository for hosting Helm Charts. Artifactory by JFrog is a one-stop shop for all your build artifact storage needs. In addition to being able to manage container images and Helm Charts, it can also manage RubyGems, NPM modules, or nearly any other sort of build artifact that you'd like to publish. These self-hosted options require administration and maintenance, so they are more labor-intensive solutions, but each is a great choice if you'd like to take image hosting into your own hands. Publishing to Other Registries If you choose to use a registry hosted somewhere other than dockerhub, your process for publishing images will change slightly. You'll still use the same tools but when tagging your image, the instructions will be slightly different. You will need to login to your preferred provider using docker login and you will need to provide your registry's hostname and other required metadata in your image's tag. The process for publishing to each provider differs slightly, but here is an example using AWS Elastic Container Registry (ECR). : ` You'll notice that the login process is different and requires you to use awscli to retrieve your password and pipe it into docker login, using "AWS" as your username. This is an added security measure. AWS changes your password regularly to keep your account secure. In ECR, all images are private by default, and you must create the repository before using docker push either via the AWS console or commandline interface. The logging and tagging process will differ slightly for each provider, but most provide straightforward and clear instructions for their process when you create your registry. Refer to your chosen provider's documentation for more info. Conclusion While Docker may be the company that introduced us to Linux container images, more and more vendors and open-source projects are getting involved in the hosting of images. You are no longer limited to using a host on the public internet or run by Docker. I hope this post has helped you understand more about all the other options available to you for image hosting....

Comparing App Platforms with Heroku/Salesforce, AWS, and Vercel cover image

Comparing App Platforms with Heroku/Salesforce, AWS, and Vercel

Introduction Recently on This Dot's Build It Better show, I had the opportunity to sit down with some folks from popular platform as a service vendors. I asked them to tell me about what got them excited about their platforms, and what advice they have to offer to help viewers choose which platform is best for them. Salesforce/Heroku I spoke with Julian Duque and Mohith Shrivastava from Salesforce about their "low code" products, and how those products are enhanced by their Heroku platform for "pro code" solutions. Salesforce makes it easy for almost anyone to get started building and running cloud apps whether they're software engineers or not. The Salesforce Platform offers a wide variety of low-code products that make it simple for non-developers to build apps, and Heroku provides a streamlined set of tools and services for running custom software applications using a wide array of application development frameworks and programming languages. I personally really love the simplicity and flexibility of the Heroku platform. I've used it for tons of projects over the years. You can host almost any application, built using any language or framework, using Heroku's buildpack technology. Heroku Buildpacks are sets of scripts that automate your app's build and deployment steps. Official Buildpacks for a dozen different platforms are available for use and in most cases, Heroku can automatically detect which one your app needs when you deploy your code. If your stack isn't supported by an official buildpack, you can build your own or use one of the many community maintained buildpacks for languages and frameworks that don't have first party support. Another benefit of Heroku is that each Heroku app has an internal Git repository and all you need to do to deploy your code is push your code to that repository using git push. There are no additonal tools required for deployments. Not only does this simplify the process of deploying your code by hand, but it also means that Heroku is automatically compatible with any CI/CD system that supports Git, which is almot all of them by now. In addition to custom applicaiton hosting, Heroku also has PaaS integrated offerings for PostgreSQL, Redis, and Apache Kafka that can all be managed through the Heroku dashboard or CLI. Even though I'm a long time user of Heroku, I wasn't really aware of everything that Salesforce brings to the table. Heroku offers a strong platform for pro code applications, but in addition, the Salesforce Platform provides a variety of low code tools that can be used to build applications by people who aren't experienced in custom software development. These tools allow businesses to begin the digital transformation process without needing to bring on a large in-house IT staff. There are point and click tools for managing authentication and identity as well as automating workflows and building user interfaces. They even offer a product called Einstein that can imbue your workflows with AI powers. However, you don't need to worry about outgrowing the low code solutions because the Salesforce Platform can also be integrated with pro code applications hosted in the Heroku ecosystem. This makes Salesforce/Heroku a great platform that businesses can rely on all the way through their digital transformation process. Technology isn't the only thing that sets Salesforce and Heroku apart from their competition. They also provide a couple of huge documentation libraries. For the Salesforce Platform, you can head to their Salesforce Trailhead site. Trailhead offers interactive courses and learning tracks that will teach you how to build applications from the ground up on the Salesforce Platform. Heroku also has an expansive documentation library that not only applies directly to the Heroku platform, but I've used their documentation personally many times to assist in resolving problems with my applications on other platforms. The Heroku documentation site is not only comprehensive, but it's also easier to consume than that of many of their competitors (I'm looking at you Amazon). And finally, when documentation isn't enough, Heroku and Salesforce also have excellent support teams who will work quickly to resolve any problems you're experiencing with their platform, and in many cases they can act proactively before you are aware you have a problem. Vercel I also spoke with Lee Robinson from Vercel. Vercel is a platform that's quite similar to Heroku in a lot of ways. However they are laser focused on providing a great hosting platform for your Jamstack applications. While Heroku can support a nearly limitless number of programming languages and application frameworks, Vercel is focused on providing the best possible experience for "serverless" Javascript apps. These are apps that use a hybrid or static JavaScript framework for building frontends and backends that are powered by NodeJS serverless functions. Serverless functions written in Python, Go, or Ruby are also supported, but there are no options for supporting functions written in languages that aren't officially supported. Compared to Heroku's flexibility, one might take this to mean that Vercel is an inferior platform, but this isn't the case at all. What Vercel doesn't offer in terms of flexibility, they make up for in developer experience. Where Heroku provides the simplicity of being able to effortlessly scale your applications by dragging a slider, Vercel takes the simplicity to the extreme and automagically scales your applications without needing to ever even use the dashboard or CLI. Not only do they completely automate and manage all the complexities of scaling your app to meet the demands of your users, you also get the benefit of having the Vercel Edge Network CDN to ensure your app is always available and performant no matter where your users are located geographically. This is all part of every single app hosted on Vercel, even the free tier! Vercel also provides additional tools to help you supercharge your development workflows and improvement cycles. "Develop. Preview. Ship" is Vercel's mantra. To help developers achieve this, not only do they provide Git-based deployments, but for each branch or pull request opened via version control, Vercel provides a "preview URL" which is connected to a preview version of your application that reflects the code on that branch/PR. This eliminates the need for complicated staging and QA workflows, since preview URLs provide isolated environments for testing and demoing new features. Another mantra Lee shared with me is the idea that "developers are scientists." As developers, we can use data to inform how we build the solutions we work on, but often that data can be cumbersome or difficult to obtain. Vercel simplifies the data collection process by offering a high quality analytics platofrm to help you understand how your applicaiton performs, not only in terms of response performance but also tracking frontend user experience metrics like layout shift and input delay. Being able to easily collect and visualize these metrics allows you to really be a scientist and always be able to justify priorities and improvements to your products with real user data. Another interesting aspect of Vercel is that they've also created a NodeJS application development framework in-house called Next.js that is meant to pair perfectly with their platform. It provides a "zero-configuration" framework for building applications with NodeJS and React. It's an incredibly flexible platform that can support the simplest one-page statically rendered applications, but also can support request-time server-side frontend rendering and custom backend API endpoints supproted by Vercel's serverless functions. To help new and experienced developers alike, Vercel offers a library of starter projects using Next.js and/or other JavaScript frameworks you can use to get your project started with just a few button clicks. Amazon Web Services I spoke with Nader Dabit from Amazon about their new Amplify platform. Amazon has been the biggest player in the PaaS marketplace for well over a decade now. Most developers have used an EC2 virtual server or stored application assets and uploads in S3. What developers may not know is that Amazon offers more than 200 different services for use by developers and other business users. Ec2 and S3 are pretty simple and straightforward, but branching out into the broader ecosystem or learning to tie everything together can be pretty intimidating. This isn't a big deal for companies like Netflix or AirBnB who can afford to bring in devops engineers that are already AWS experts, but historically it's been a lot more difficult for less exprienced developers to take full advantage of what AWS has to offer. With Amplify, the AWS team is hoping to demystify the process and give new and experienced developers a way to work with the core AWS platform in a more streamlined way. Instead of having to udnerstand which service to use out of a list of 200+ services with intimidating names, Amplify selects a smaller subset of these services and gives them less esoteric names. So Amazon Cognito becomes "Authentication" and AWS Lambda becomes "Functions". They also provide simplified client libraries over the traditional AWS SDK that are compatible with JavaScript, Android, iOS and Flutter. Another neat thing about the Amplify platform is that they, like Salesforce, are steering users toward Amazon's low code tools like AWS AppSync and API Gateway, and making it easier for developers to integrate with AWS tools for things like AI/ML predictions and PubSub. Also like Salesforce, if developers outgrow the lowcode tools, it's easier than ever to expand out to the boader ecosystem and some of the more specialized services that amazon offers. In addition to making it easy to build your application's backend with little or no code, Amplify also offers the frontend components you need to build interactive web or mobile apps. Amplify UI components are available for React, Angular, Vue and more. And of course, on top of the simplified Amplify toolchain, AWS still provides the same 200+ services they've traditionally offered. So if you outgrow Amplify, or need services that aren't compatible with it, you can always integrate offerings outside of the Amplify ecosystem with other AWS services. Another thing I really like about Amplify, and AWS in general, is the pricing. All of the Amplify services have a free tier. This makes it useful for hobby projects or to keep development costs low before you launch your applications. Also, it's important to note that the other services like Heroku and Vercel are often based on AWS themselves (. As such, buying services direct from AWS will usually save you at least a little bit of money over using a more managed service. Conclusion Developers have a ton of choices when they are choosing a platform to build their applications on. All of the vendors I spoke with have compelling solutions that will make your life as a developer better. I always personally reach for platforms like Heroku or Vercel first since they're quick and easy to get started with, but it's clear that AWS has taken note of that and is trying to close that gap. So really, there's not a bad choice if these are your options. I hope I've explained them well enough so you can choose which one suits your project the best!...

An Introduction to Laravel Queues and Temporary URLs cover image

An Introduction to Laravel Queues and Temporary URLs

Laravel is a mature, robust, and powerful web framework that makes developing PHP applications a breeze. In particular, I want to demonstrate how to create a website that can be used to convert videos online using queue jobs for processing and temporary URLs for downloading the converted files. This article is aimed at those who aren’t very familiar with Laravel yet. Prerequisites There are many ways to set up Laravel, and which is the best method may depend on your operating system or preference. I have found Laravel Herd to be very easy to use if you’re using Windows or macOS. Herd is a Laravel development environment that has everything you need with minimal configuration required. Command-line tools are installed and added to your path, and background services are configured automatically. If you’re developing on Linux then Herd is not an option. However, Laravel Sail works for all major operating systems and uses a Docker based environment instead. You can find a full list of supported installation methods in the Laravel documentation. To keep things simpl,e this article assumes the use of Herd, though this won’t make a difference when it comes to implementation. You will also need a text editor or IDE that has good PHP support. PhpStorm is a great editor that works great with Laravel, but you can also use VSCode with the Phpactor language server, and I’ve found Phpactor to work quite well. Project Setup With a development environment setup, you can create a new Laravel project using composer, which is the most popular package manager for PHP. Herd installs composer for you. composer installs dependencies and lets you run scripts. Let’s create a Laravel project using it: ` Once that is done you can navigate into the project directory and start the server with artisan: ` Awesome! You can now navigate to http://localhost:8000/ and see the Laravel starter application’s welcome page. Artisan is the command-line interface for Laravel. It comes with other utilities as well such as a database migration tool, scripts for generating classes, and other useful things. Uploading Videos Using Livewire Livewire is a library that allows you to add dynamic functionality to your Laravel application without having to add a frontend framework. For this guide we’ll be using Livewire to upload files to our server and update the status of the video conversion without requiring any page reloads. Livewire can be installed with composer like so. ` With it installed we need to make a Livewire component now. This component will act as the controller of our video upload page. ` With that done you should see two new files were created according to the output of the command, one being a PHP file and the other being a Blade file. Laravel has its own HTML template syntax for views that allow you to make your pages render dynamically. For this demo we’ll make the video conversion page render at the root of the site. You can do this by going to routes/web.php and editing the root route definition to point to our new component. ` However, if we visit our website now it will return an error. This is due to the app template being missing, which is the view that encapsulates all page components and contains elements such as the document head, header, footer, etc. Create a file at resources/views/components/layouts/app.blade.php and put the following contents inside. This will give you a basic layout that we can render our page component inside of. ` The {{ $slot }} string in the main tag is a Blade echo statement. That is where our Livewire component will be injected when loading it. Now, let’s edit the Livewire component’s template so it has something meaningful in it that will allow us to verify that it renders correctly. Edit resources/views/livewire/video-uploader.blade.php and put in the following: ` With that done you can go to the root of the site and see this hello message rendered inside of a box. Seeing that means everything is working as it should. We may as well delete the welcome template since we’re not using it anymore. This file is located at resources/views/welcome.blade.php. Now, let’s go ahead and add uploading functionality. For now we’ll just upload the file into storage and do nothing with it. Go ahead and edit app/Livewire/VideoUploader.php with the following: ` This will only allow uploading files with video file MIME types. The $video class variable can be wired inside of the component’s blade template using a form. Create a form in resources/views/livewire/video-uploader.blade.php like so: ` You will note a wire:submit attribute attached to the form. This will prevent the form submission from reloading the page and will result in Livewire calling the component’s save method using the video as a parameter. The $video property is wired with wire:model="video". Now you can upload videos, and they will be stored into persistent storage in the storage/app/private directory. Awesome! Increase the Filesize Limit If you tried to upload a larger video you may have gotten an error. This is because the default upload size limit enforced by Livewire and PHP is very small. We can adjust these to accommodate our use-case. Let’s start with adjusting the Livewire limit. To do that, we need to generate a configuration file for Livewire. ` All values in the generated file are the defaults we have been using already. Now edit config/livewire.php and make sure the temporary_file_upload looks like this: ` The rules key allows us to change the maximum file size, which in this case is 100 megabytes. This alone isn’t good enough though as the PHP runtime also has a limit of its own. We can configure this by editing the php.ini file. Since this article assumes the use of Herd, I will show how that is done with it. Go to Herd > Settings > PHP > Max File Upload Size > and set it to 100. Once done you need to stop all Herd services in order for the changes to take effect. Also make sure to close any background PHP processes with task manager in-case any are lingering, as this happened with me. Once you’ve confirmed everything is shut off, turn on all the services again. If you’re not using Herd, you can add the following keys to your php.ini file to get the same effect: ` Creating a Background Job Now, let’s get to the more interesting part that is creating a background job to run on an asynchronous queue. First off, we need a library that will allow us to convert videos. We’ll be using php-ffmpeg. It should be noted that FFmpeg needs to be installed and accessible in the system path. There are instructions on their website that tell you how to install it for all major platforms. On macOS this is automatic if you install it with homebrew. On Windows you can use winget. On macOS and Linux you can confirm that ffmpeg is in your path like so: ` If a file path to ffmpeg is returned then it’s installed correctly. Now with FFmpeg installed you can install the PHP library adapter with composer like so: ` Now that we have everything we need to convert videos, let’s make a job class that will use it: ` Edit app/Jobs/ProcessVideo.php and add the following: ` To create a job we need to make a class that implements the ShouldQueue interface and uses the Queueable trait. The handle method is called when the job is executed. Converting videos with php-ffmpeg is done by passing in an input video path and calling the save method on the returned object. In this case we’re going to convert videos to the WebM container format. Additional options can be specified here as well, but for this example we’ll keep things simple. One important thing to note with this implementation is that the converted video is moved to a file path known by the livewire component. Later and to keep things simple we’re going to modify the component to check this file path until the file appears, and while for demo purposes this is fine, in an app deployed at a larger scale with multiple instances it is not. In that scenario it would be better to write to a cache like Redis instead with a URL to the file (if uploaded to something like S3) that can be checked instead. Now let’s use this job! Edit app/Livewire/VideoUploader.php and let’s add some new properties and expand on our save method. ` How this works is we tell the job where it can find the video and tell it where it should output the converted video when it’s done. We have to make the output filename be the same as the original with just the extension changed, so we use pathinfo to extract that for us. The ProcessVideo::dispatch method is fire and forget. We aren’t given a handle of any kind to be able to check the status of a job out of the box. For this example we’ll be waiting for the video to appear at the output location. To process jobs on the queue you need to start a queue worker as jobs are not processed in the same process as the server that we are currently running. You can start the queue with artisan: ` Now the queue is running and ready to process jobs! Technically you can upload videos for conversion right now and have them be processed by the job, but you won’t be able to download the file in the browser yet. Generating a Temporary URL and Sending it with Livewire To download the file we need to generate a temporary URL. Traditionally this feature has only been available for S3, but as of Laravel v11.24.0 this is also usable with the local filesystem, which is really useful for development. Let’s add a place to render the download link and the status of the job. Edit resources/views/livewire/video-uploader.blade.php and add a new section under the form: ` Note the wire:poll attribute. This will cause the Blade echo statements inside of the div to refresh occasionally and will re-render if any of them changed. By default, it will re-render every 2.5 seconds. Let’s edit app/Livewire/VideoUploader.php to check the status of the conversion, and generate a download URL. ` Every time the page polls we check if the video has appeared at the output path. Once it’s there we generate the link, store it to state, and pass it to the view. Temporary URLs are customizable as well. You can change the expiration time to any duration you want, and if you’re using S3, you can also pass S3 request parameters using the optional 3rd argument. Now you should be able to upload videos and download them with a link when they’re done processing! Limitations Although this setup works fine in a development environment with a small application, there are some changes you might need to make if you plan on scaling beyond that. If your application is being served by multiple nodes then you will need to use a remote storage driver such as the S3 driver, which works with any S3 compatible file storage service. The same Laravel API calls are used regardless of the driver you use. You would only have to update the driver passed into the Storage facade methods from local to s3, or whichever driver you choose. You also wouldn’t be able to rely on the same local filesystem being shared between your job workers and your app server either and would have to use a storage driver or database to pass files between them. This demo uses the database driver for simplicity’s sake, but it's also worth noting that by default, queues and jobs use the database driver, but SQS, Redis, and Beanstalkd can also be used. Consider using these other drives instead of depending on how much traffic you need to process. Conclusion In this article, we explored how to utilize queues and temporary URLs to implement a video conversion site. Laravel queues allow for efficient processing of long-running tasks like video conversion in a way that won’t bog down your backend servers that are processing web requests. While this setup works fine for development, some changes would need to be made for scaling this such as using remote storage drivers for passing data between the web server and queue workers. By effectively leveraging Laravel’s features, developers can create robust and scalable applications with relative ease....

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co