# Create your joomla website using Docker A series of 6 article(s), in reading order. # FrankenPHP, a modern application server for PHP > Learn how to set up a Joomla website using Docker and FrankenPHP, a modern PHP server that's up to 3.5x faster than PHP-FPM. Includes a simple compose.yaml setup. --- ![FrankenPHP](/img/v2/frankenphp.webp) **TL;DR** This article tries out FrankenPHP, a modern PHP application server claimed to be up to 3.5x faster than PHP-FPM, by running a ready-made Joomla setup (by Alexandre Elisé) via `docker compose pull && docker compose up`. It walks through the slow first-run MySQL connection warmup and accessing the resulting HTTPS Joomla site on a dynamically assigned port. Based on [their documentation](https://speakerdeck.com/dunglas/the-php-revolution-is-underway-frankenphp-1-dot-0-beta), [FrankenPHP](https://frankenphp.dev/) is 3.5 times faster than PHP FPM. It has reached a stable release since, and it's certainly worth playing with when developing locally. So, what does a real site look like on it? Let's run a full Joomla installation and find out. ## What FrankenPHP gives you A complete Joomla site, in https, served by FrankenPHP, started with two commands: > Screenshot — https\://localhost:54408 > > ![Joomla is now running on FrankenPHP](./images/frankenphp_joomla_homepage.webp) You will perhaps not see a major increase in speed on your machine since you are the only visitor, but it is nice to think that you are surfing so fast locally. Now the honest part of the deal, before you start: > **Ouch, it's terribly slow to run:** > > To be honest, before being able to see my Joomla localhost homepage, I waited more than 15 minutes (the first time). I would never have waited so long if I hadn't had to finish this chapter. ## Why it works * FrankenPHP is a modern PHP application server built on top of the Caddy web server: one process serves PHP *and* handles https, instead of the classic *"web server talks to PHP-FPM"* pair. * Because it keeps the PHP runtime alive between requests, its authors measure it up to 3.5 times faster than PHP-FPM. * You don't have to assemble any of this: [Alexandre Elisé](https://github.com/alexandreelise) publishes a ready-made FrankenPHP + Joomla image, so the whole setup is a `compose.yaml` file and a `docker compose up`. ## Getting it running I invite you to play with it on your development machine (unless you have your own servers; you certainly won't be able to use FrankenPHP at your hosting company). Go, for instance, in your `/tmp/joomla` folder and create the `compose.yaml` file below. The source of the project is and Alexandre's `Getting Started` readme file covers the other ways of running it. > **Don't build the image yourself:** > > My suggestion is to replace the `compose.yaml` file with the one below. This way, you'll reuse the image publicly made available by Alexandre and do not need to build it yourself (much faster): > > **compose.yaml** > > ```yaml > services: > > joomla: > image: alexandreelise/frankenphp-joomla:hardened-mysql-0.1.1 > restart: always > ports: > - 80:80 > - 443:443 > environment: > - MYSQL_ROOT_PASSWORD=test > - JOOMLA_DB_HOST=db > - JOOMLA_DB_USER=exampleuser > - JOOMLA_DB_PASSWORD=examplepass > - JOOMLA_DB_NAME=exampledb > volumes: > - joomla:/app/public > depends_on: > - db > > db: > image: mysql:8.2 > environment: > - MYSQL_DATABASE=exampledb > - MYSQL_USER=exampleuser > - MYSQL_PASSWORD=examplepass > - MYSQL_ROOT_PASSWORD=test > volumes: > - db:/var/lib/mysql > > volumes: > joomla: > db: > ``` By running `docker compose pull`, Docker will download the two images; the one with FrankenPHP and Joomla and the one for MySQL. Depending on the speed of your Internet connection, this will take a few seconds; only the first time. Then, you just need to create containers based on the images by running `docker compose up`. ## What happens during those long minutes You'll start getting logs messages on the console *(because, here, you have not used the `--detach` flag for the illustration)*: ![Running FrankenPHP](./images/running_frankenphp.webp) > **Please wait until MySQL is ready:** > > You now have to wait **a few minutes** before the database connection is ready. You will have the feeling that the installation fails due to a lot of `[ERROR] Connection refused` lines but just wait. The reason is that Joomla will try to connect to MySQL while the MySQL container is not ready to handle connections. You will then see a lot of `[ERROR] Connection refused`. Stay patient and after a while, you will get this: ![Joomla has been installed](./images/frankenphp_joomla_installed.webp) > **Logs can be different in your version:** > > Depending on the version of the used Docker images, scripts and version of Joomla, the logs statements can differ in time. When everything has been successfully done, just browse to `https://localhost:443` to get your Joomla site running on FrankenPHP. To get access to your administrator page, browse to `https://localhost:443/administrator`. Credentials to use can be retrieved in the logs as shown by the red arrow on the image above. You can retrieve them also using this command: `docker compose logs | grep -i "Here are your Joomla credentials:"`. > **FrankenPHP is using SSL and thus https:** > > Please note that FrankenPHP is delivering your site using `https`. The way Alexandre has built his script, the port number is not fixed. To determine which port to use, start a new Linux console and run `docker container list` to get the list of running containers. You'll see the port to use to access your FrankenPHP site in the `PORTS` column. This is also displayed in the `Docker Desktop` Windows application; go to the list of containers to get the port. ## Under the hood (skip this if you just want to use it) Those `Connection refused` lines are not a FrankenPHP problem: they're the classic *"my container is started, therefore my service is ready"* mistake. A container that has started is not a database that accepts connections, and nothing in `docker compose up` tells the difference. *This is exactly the problem healthchecks are made for; [Get health information from your running containers](/blog/docker-healthy) shows how to know whether a container is really ready, and not just started.* ## Conclusion For a local development machine, FrankenPHP is worth the detour: one image, https out of the box, and a PHP runtime that stays warm between requests. The price to pay is a first start that takes a good quarter of an hour; after that, `docker compose up` is instantaneous. If you'd rather have a Joomla site running right now with the classic Apache + PHP-FPM stack, [Start Joomla with Docker in just a few clicks](/blog/docker-joomla-right-to-the-point) gets you there in a couple of minutes. --- # Create your Joomla website using Docker - Part 1 > Learn how to quickly set up your Joomla website locally using Docker. This step-by-step guide covers Docker Compose, database configuration (MySQL/MariaDB), and running your services. --- ![Create your Joomla website using Docker - Part 1](/img/v2/joomla.webp) **TL;DR** This article details how to rapidly set up a local Joomla website using Docker by configuring a `compose.yaml` file to orchestrate the application and database services. It explains critical steps for ensuring data persistence through volume synchronization, managing ports for multiple projects, and customizing database connections. The tutorial concludes with a CLI one-liner to deploy the entire environment in seconds. In this article, we will learn how to use Docker to install Joomla on your localhost and start a new website **in seconds**. I will use a Linux console *(I'm running WSL on my Windows computer and I have chosen Ubuntu for my distribution)* but since Docker can also be used on Windows, you can run exactly the same commands in an MS-DOS / PowerShell console. **Quick Jump:** [Two Minutes, One File](#two-minutes-one-file-your-joomla-is-running) | [Bonus - CLI One-Liner](#bonus---install-joomla-with-a-cli-one-liner) If you don't have Docker yet, please consult my "[Install Docker and play with PHP](/blog/install-docker)" article to get more information about Docker and an easy introduction guide. **We will assume in this article that you have Docker, and you're working under Linux or using WSL.** ## Two minutes, one file, your Joomla is running Click on the `Generate install script` below and copy/paste the script in a terminal: **Project: /tmp/joomla** **compose.yaml** ```yaml services: joomla: image: joomla restart: always ports: - 8080:80 environment: - JOOMLA_DB_HOST=joomladb - JOOMLA_DB_PASSWORD=example joomladb: image: mysql:8.0.13 restart: always environment: - MYSQL_ROOT_PASSWORD=example ``` > **Note:** > > Now, simply run 'docker compose up -d' to start the project then open your > browser and surf to Run `docker compose up --detach`, wait for the images to download, then open `http://localhost:`8080: > Screenshot — http\://localhost:8080 > > ![Joomla installer](./images/joomla_installation_screen.webp) Answer the installer's questions and, a few seconds later, you're here: > Screenshot — http\://localhost:8080/aministrator/index.php > > ![Joomla administration dashboard](./images/admin_dashboard.webp) A complete Joomla website, on your machine, with a database — and not a single file of Joomla or MySQL installed on your disk. ## What just happened That one command asked Docker to read the `compose.yaml` file and, from it: 1. a `joomla_default` network is created, 2. the `joomla-joomladb-1` container is created (this is your database server) and 3. the `joomla-joomla-1` container is created too (this is your Joomla service). The two containers share the network, which is how Joomla can reach the database by its service name. The rest of this article explains that file line by line, then goes much further: persisting your site on disk, running CLI commands, changing ports, switching to PostgreSQL or MariaDB. ## Before starting **As you know, to be able to run a CMS like Joomla we need three things + 1:** 1. We need a web server like **Apache** or **nginx** 2. We need a database service like **MySQL**, **MariaDB** or **PostgreSQL** or any other supported databases and 3. We need **PHP** and 4. Of course, we need **Joomla** In terms of Docker: we need three services. ## Docker compose When you need many services (Apache should be able to communicate with PHP and PHP should be able to request data from MySQL), you need to configure a special file called `compose.yaml`. That file should be placed in the root of the project and will define the list of services required and how they will collaborate together. You will find an example of the `compose.yaml` file on the Joomla image description page: *search for `docker-compose` on this page.* > **The Docker Joomla image is built on PHP and Apache:** > > It's for sure too technical now but if you click on the > > link, you can see that the Docker image for Joomla 6.1 is built on a > `php:8.4-apache` image. This means that using this image, you will get PHP, > Apache and Joomla, altogether in a single image. ## Download images Let's go back to the generation script from the beginning of this article and look at what it actually did. To make things as clear as possible, here is my temporary folder content once I've run it: ```bash cd /tmp/joomla $ ls -alh total 12K drwxr-xr-x 2 christophe christophe 4.0K Jan 4 15:31 . drwxrwxrwt 12 root root 4.0K Jan 4 15:31 .. -rw-r--r-- 1 christophe christophe 279 Jan 4 15:31 compose.yaml ``` So, I just have one file, and this is the newly, created, `compose.yaml` file. > **`docker compose up --detach` is one of the most important commands to know. It asks Docker to proceed the `compose.yaml` file and run services. In short: run your website.:** Docker will start downloading `joomla` and `joomladb`, the two services mentioned in the `compose.yaml` file. **compose.yaml** ```yaml services: // highlight-next-line joomla: [...] // highlight-next-line joomladb: [...] ``` You will obtain something like this, please wait until everything is downloaded. ```bash $ docker compose up --detach [+] Running 16/35 ⠹ joomladb 12 layers [⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀] 0B/0B Pulling 19.3s ⠧ 177e7ef0df69 Waiting 15.8s ⠧ cac25352c4c8 Waiting 15.8s ⠧ 8585afabb40a Waiting 15.8s [...] ⠹ joomla 21 layers [⣿⣿⣦⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣿⣿⣿⣿⣀⠀⠀] 94.59MB/155.9MB Pulling 19.3s ✔ 578acb154839 Pull complete 11.3s ✔ c053f6f43c12 Pull complete 11.9s ⠋ 65cebbf4d847 Downloading [==============> ] 68.41MB/104.4MB 16.1s ✔ 34045bc93960 Download complete 1.0s [...] ``` At the end, once images have been downloaded, the console will show something like this: ```bash $ docker compose up --detach [+] Running 35/35 ✔ joomladb 12 layers [⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿] 0B/0B Pulled 84.9s ✔ 177e7ef0df69 Pull complete 26.9s ✔ cac25352c4c8 Pull complete 27.5s ✔ 8585afabb40a Pull complete 28.2s [...] ✔ joomla 21 layers [⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿] 0B/0B Pulled 146.4s ✔ 578acb154839 Pull complete 11.3s ✔ c053f6f43c12 Pull complete 11.9s ✔ 65cebbf4d847 Pull complete 31.2s [...] [+] Running 3/3 ✔ Network joomla_default Created 0.3s ✔ Container joomla-joomladb-1 Started 52.9s ✔ Container joomla-joomla-1 Started 38.8s ``` The two images have been downloaded, and the network plus the two containers listed earlier have been created. At this stage, your site is already being installed. Go to the URL `http://127.0.0.1:`8080 to view it (make sure to use the `http` protocol and not `https`). > **Not yet ready:** > > You may get an error page `ERR_EMPTY_RESPONSE`; this is because, for example, > MySQL is not yet fully loaded and Joomla has to wait for it before it can > display the installation page. In this case, please wait a little longer ... > or read the rest of this article. > **Solve it by adding a depends\_on condition:** > > So Joomla could become ready before the database, and that's not good. Indeed, Joomla should be able to create its database, add tables and records but if MySQL is not yet ready, we'll have a problem. > > The solution provided by Docker is the notion of `healthcheck`. We have to define how Docker can *know* that MySQL is *healthy*. Then we'll ask Joomla to wait until the database layer is *healthy*. > > **compose.yaml** > > ```yaml > services: > // highlight-next-line > joomla: > [...] > // highlight-next-line > depends_on: > // highlight-next-line > joomladb: > // highlight-next-line > condition: service_healthy > > // highlight-next-line > joomladb: > [...] > // highlight-next-line > healthcheck: > // highlight-next-line > test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"] > // highlight-next-line > timeout: 20s > // highlight-next-line > retries: 10 > ``` ### Why joomla-joomlaxxx names We didn't give your project a name, we just created a `compose.yaml` file in your `/tmp/joomla` folder. So, Docker has named your project using the folder name (`joomla`) concatenated to the service name (refer to the `compose.yaml` file: we have two services, one called `joomladb` and one called `joomla`). That is why... Let us introduce a minor, optional change: we will give a name to your Docker project and containers. edit the `compose.yaml` file and add a line with `name: xxxx` where `xxxx` is the name of your choice. Do the same but using `container_name` this time for the two services; for instance: **compose.yaml** ```yaml // highlight-next-line name: kingsbridge services: joomla: // highlight-next-line container_name: kingsbridge-app [...] joomladb: // highlight-next-line container_name: kingsbridge-db [...] ``` We won't be restarting your Docker containers yet. For now, the kingsbridge name won't be considered. For this to be the case, we would need to launch `docker compose down` followed by `docker compose up --detach`, but let us wait a little longer before doing so. ## Docker images If you're curious, you can run the `docker image list` command to get the list of Docker images already downloaded on your machine. ```bash $ docker image list REPOSITORY TAG IMAGE ID CREATED SIZE joomla latest 882b2151d890 2 days ago 663MB mysql 8.0.13 102816b1ee7d 4 years ago 486MB ``` Ok, so, Docker has downloaded Joomla (in its *latest* version) and MySQL (version 8.0.13). **How to use a specific version of Joomla?** By default, when we don't specify any version number (which isn't recommended for production), Docker will download the version known as `latest`. `latest` is what Docker calls a "tag". To ensure your setup is stable and predictable, it's best to use a specific version tag. You can find all available tags on the [official Joomla Docker Hub page](https://hub.docker.com/_/joomla/tags). For example, to use Joomla version 5.3.3 with PHP 8.2, you would change the `image` line in your `compose.yaml` like this: **compose.yaml** ```yaml services: joomla: image: joomla:5.3.3-php8.2-apache [...] ``` ## Docker containers We're almost done. Please run `docker container list` to get the list of containers created by Docker: ```bash $ docker container list CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0798f8f25d2b joomla "/entrypoint.sh apac…" 8 minutes ago Up 5 minutes 0.0.0.0:8080->80/tcp joomla-joomla-1 7b7fcd3809b0 mysql:8.0.13 "docker-entrypoint.s…" 8 minutes ago Up 7 minutes 3306/tcp, 33060/tcp joomla-joomladb-1 ``` We have two running containers (your two services). Pay attention to the `PORTS` column: your `joomla` container is listening on the port 8080 and your `mysql` container is listening on port `3306`. Hey, port 8080, does that mean anything to you? That is a port for a web page, isn't it? Let us try by starting your favorite browser and navigating to `http://localhost:`8080 (*or `http://127.0.0.1:8080`, it's strictly the same*) and... Wow! The Joomla installer shown at the beginning of this article is waiting for you. ## Install Joomla Back to `http://localhost:`8080 and we will continue the installation. On the first screen, just select your preferred language then enter a name for your Joomla site: > Screenshot — http\://localhost:8080 > > ![Name your site](./images/admin_step_1.webp) When Joomla will ask for your admin credentials, just fill in the value of your choice: > Screenshot — http\://localhost:8080 > > ![Login data](./images/joomla_admin_login_data.webp) **But, for the database configuration, here you need to be strict:** * The database type should be `MySQLi` (since we're using MySQL), * The name of the host should be `joomladb` (the name we have chosen for the database service), * The username should be `root` (default value) and * The password for that user should be `example` > **Why these values?:** > > These values can be retrieved inside the `compose.yaml` file. If you have named your database service something other than `joomladb`, then please use the name you have chosen. **compose.yaml** ```yaml services: [...] // highlight-next-line joomladb: [...] environment: // highlight-next-line - MYSQL_ROOT_PASSWORD=example ``` > Screenshot — http\://localhost:8080 > > ![Joomla Database configuration](./images/joomla_admin_database_configuration.webp) And, after a few seconds, tadaaa... > Screenshot — http\://localhost:8080 > > ![Joomla installation is done](./images/joomla_admin_ready.webp) The administrator page: > Screenshot — http\://localhost:8080/administrator > > ![Joomla installation is done](./images/joomla_start_administrator.webp) And behind it, the administrator dashboard shown at the beginning of this article. **Congratulations, you have successfully installed a fresh Joomla website using Docker!** ## Everything is done in RAM Let us leave a few minutes the browser and go back to your Linux console. In the previous chapter, we have installed Joomla so we should have Joomla on your computer, right? Let us verify by returning to our Linux console: ```bash $ pwd /tmp/joomla $ ls -alh total 12K drwxr-xr-x 2 christophe christophe 4.0K Jan 4 15:31 . drwxrwxrwt 12 root root 4.0K Jan 4 15:31 .. -rw-r--r-- 1 christophe christophe 279 Jan 4 15:31 compose.yaml ``` Oh? Nothing... **Nothing has been downloaded in your folder.** You don't have Joomla on your computer. How is this possible? We will discuss this later but yes, by default with Docker, everything happens in memory, nothing on the disk. If you can't wait, please read my "[Share data between your running Docker container and your computer](/blog/docker-volume)" article to better understand why and how. ## Play with containers Still on your console, type again `docker container list`: ```bash $ docker container list CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0798f8f25d2b joomla "/entrypoint.sh apac…" 8 minutes ago Up 5 minutes 0.0.0.0:8080->80/tcp joomla-joomla-1 7b7fcd3809b0 mysql:8.0.13 "docker-entrypoint.s…" 8 minutes ago Up 7 minutes 3306/tcp, 33060/tcp joomla-joomladb-1 ``` Pay attention this time to the last column, called `NAMES`. We have thus two containers, one named `joomla-joomla-1` and one name `joomla-joomladb-1`. We will stop them by running `docker compose down`: ```bash $ docker compose down [+] Running 3/3 ✔ Container joomla-joomla-1 Removed 2.6s ✔ Container joomla-joomladb-1 Removed 4.2s ✔ Network joomla Removed ``` If you go back to `http://localhost:`8080 with your browser and refresh the page; the site didn't exist anymore. Run `docker compose up --detach` again. ```bash $ docker compose up --detach ``` Surf to the site again, refresh the page and the site is, oh, not yet responding. Wait a few\... a few more... and after a certain period, the site will be there. Why? Docker must (re)start two services, the Joomla one and the database server. Initialization of both services may take a few seconds, and the database service must be up so that Joomla can continue its initialization. You can see this by running `docker compose logs --follow` (press `CTRL`+`C` to quit). ```bash $ docker compose logs --follow ``` Once Joomla is ready, you will get the installation wizard of Joomla... just like the first time. So, by running `docker compose down` you have lost your work. ```bash $ docker compose down ``` > **Danger:** > > As mentioned earlier, everything is done in RAM. By stopping a Docker container, you will lose everything not saved on your computer. It's great for playing/learning but not what you expect when you're developing a real site. > **Information:** > > Remember the change we made earlier. We had added the name kingsbridge as the project name in your `compose.yaml` file, and we've named the two containers. You can see that after relaunching `docker compose up`, this time it's no longer `joomla-joomlaxxxx` but kingsbridge-app and kingsbridge-db. This because changes made to the yaml file are processed only after a `down / up` command. If you modify the yaml file, you should restart Docker containers. ## Synchronize with your computer We will now require that Docker store files/folders on your computer. **We wish two things:** * We want the entire website to be saved on your hard disk and * we want the database to be saved on the hard disk as well. To do this, please edit the `compose.yaml` file and add the highlighted lines below: **compose.yaml** ```yaml name: kingsbridge services: joomla: image: joomla restart: always ports: - 8080:80 environment: - JOOMLA_DB_HOST=joomladb - JOOMLA_DB_PASSWORD=example depends_on: joomladb: condition: service_healthy // highlight-next-line user: ${UID:-1000}:${GID:-1000} // highlight-next-line volumes: // highlight-next-line - ./site_joomla:/var/www/html joomladb: image: mysql:8.0.13 restart: always environment: - MYSQL_ROOT_PASSWORD=example healthcheck: test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"] timeout: 20s retries: 10 // highlight-next-line user: ${UID:-1000}:${GID:-1000} // highlight-next-line volumes: // highlight-next-line - ./db:/var/lib/mysql ``` The `/var/www/html` folder of the Joomla service should be synchronized with the `site_joomla` subfolder on your computer. This is for the Joomla website. And the `/var/lib/mysql` folder of the MySQL service should be synchronized with your local `db` subfolder. > **Please create folders first:** > > Make sure to, first, create these two directories on your computer, so folder's permissions will be correct! > > ```bash > $ mkdir site_joomla db > ``` The two lines `user: ${UID:-1000}:${GID:-1000}` are really important and inform Docker to reuse your local user (the one used on your Linux session). > **The user UID/GID:** > > This part is crucial: if you type `id -u` and `id -g` on Linux, you'll get your own user\_id and group\_id (aka UID and GID). Most of the time, it'll be `1000` for both but ... sometimes not. > > The syntax `user: ${UID:-1000}:${GID:-1000}` means to use `1000` as default value except if OS variables `UID` and `GID` exists and in that case, use their values. Just check your folder's content: ```bash $ pwd /tmp/joomla $ ls -alh total 20K drwxr-xr-x 4 christophe christophe 4.0K Jan 4 15:57 . drwxrwxrwt 12 root root 4.0K Jan 4 15:31 .. -rw-r--r-- 1 christophe christophe 279 Jan 4 15:31 compose.yaml drwxr-xr-x 2 christophe christophe 4.0K Jan 4 15:57 db drwxr-xr-x 2 christophe christophe 4.0K Jan 4 15:57 site_joomla ``` Run Docker again but first, make sure the previous containers are removed (not only stopped): `docker compose kill`. ```bash $ docker compose kill [+] Killing 2/2 ✔ Container kingsbridge-db Killed 2.1s ✔ Container kingsbridge-app Killed 1.5s ``` Please now run the following command and, as you can see, we'll first initialize UID and GID variables so we'll not meet permissions issues: ```bash $ UID=$(id -u) GID=$(id -g) docker compose up --detach [+] Running 3/3 ✔ Network kingsbridge_default Created 0.3s ✔ Container kingsbridge-db Started 3.3s ✔ Container kingsbridge-app Started ``` And you can, already, run `ls` again to see that, yes, your local `site_joomla` and `db` folders are populated now. This is the result of the `volumes` entry we have added in your `compose.yaml` file. ```bash $ pwd /tmp/joomla $ ls -alh site_joomla total 132K drwxr-xr-x 19 christophe christophe 4.0K Jan 4 15:59 . drwxr-xr-x 4 christophe christophe 4.0K Jan 4 15:57 .. -rw-r--r-- 1 christophe christophe 6.8K Jan 4 15:59 .htaccess -rw-r--r-- 1 christophe christophe 18K Nov 19 19:54 LICENSE.txt -rw-r--r-- 1 christophe christophe 5.2K Nov 19 19:54 README.txt drwxr-xr-x 11 christophe christophe 4.0K Nov 19 19:54 administrator drwxr-xr-x 5 christophe christophe 4.0K Nov 19 19:54 api drwxr-xr-x 2 christophe christophe 4.0K Nov 19 19:54 cache drwxr-xr-x 2 christophe christophe 4.0K Nov 19 19:54 cli drwxr-xr-x 18 christophe christophe 4.0K Nov 19 19:54 components drwxr-xr-x 2 christophe christophe 4.0K Nov 19 19:54 files -rw-r--r-- 1 christophe christophe 6.8K Nov 19 19:54 htaccess.txt drwxr-xr-x 5 christophe christophe 4.0K Nov 19 19:54 images drwxr-xr-x 2 christophe christophe 4.0K Nov 19 19:54 includes -rw-r--r-- 1 christophe christophe 1.6K Nov 19 19:54 index.php drwxr-xr-x 10 christophe christophe 4.0K Nov 19 19:54 installation drwxr-xr-x 4 christophe christophe 4.0K Nov 19 19:54 language drwxr-xr-x 6 christophe christophe 4.0K Nov 19 19:54 layouts drwxr-xr-x 6 christophe christophe 4.0K Nov 19 19:57 libraries drwxr-xr-x 75 christophe christophe 4.0K Nov 19 19:58 media drwxr-xr-x 27 christophe christophe 4.0K Nov 19 19:54 modules drwxr-xr-x 26 christophe christophe 4.0K Nov 19 19:54 plugins -rw-r--r-- 1 christophe christophe 764 Nov 19 19:54 robots.txt.dist drwxr-xr-x 5 christophe christophe 4.0K Nov 19 19:54 templates drwxr-xr-x 2 christophe christophe 4.0K Nov 19 19:54 tmp -rw-r--r-- 1 christophe christophe 3.0K Nov 19 19:54 web.config.txt $ ls -alh db total 176M drwxr-x--- 2 christophe christophe 4.0K Jan 4 15:59 '#innodb_temp' drwxr-xr-x 7 christophe christophe 4.0K Jan 4 15:59 . drwxr-xr-x 4 christophe christophe 4.0K Jan 4 15:57 .. -rw-r----- 1 christophe christophe 56 Jan 4 15:59 auto.cnf -rw-r----- 1 christophe christophe 3.0M Jan 4 15:59 binlog.000001 -rw-r----- 1 christophe christophe 358 Jan 4 15:59 binlog.000002 -rw-r----- 1 christophe christophe 32 Jan 4 15:59 binlog.index -rw------- 1 christophe christophe 1.7K Jan 4 15:59 ca-key.pem -rw-r--r-- 1 christophe christophe 1.1K Jan 4 15:59 ca.pem -rw-r--r-- 1 christophe christophe 1.1K Jan 4 15:59 client-cert.pem -rw------- 1 christophe christophe 1.7K Jan 4 15:59 client-key.pem -rw-r----- 1 christophe christophe 5.9K Jan 4 15:59 ib_buffer_pool -rw-r----- 1 christophe christophe 48M Jan 4 15:59 ib_logfile0 -rw-r----- 1 christophe christophe 48M Jan 4 15:59 ib_logfile1 -rw-r----- 1 christophe christophe 12M Jan 4 15:59 ibdata1 -rw-r----- 1 christophe christophe 12M Jan 4 15:59 ibtmp1 drwxr-x--- 2 christophe christophe 4.0K Jan 4 15:59 joomla drwxr-x--- 2 christophe christophe 4.0K Jan 4 15:59 mysql -rw-r----- 1 christophe christophe 30M Jan 4 15:59 mysql.ibd drwxr-x--- 2 christophe christophe 4.0K Jan 4 15:59 performance_schema -rw------- 1 christophe christophe 1.7K Jan 4 15:59 private_key.pem -rw-r--r-- 1 christophe christophe 452 Jan 4 15:59 public_key.pem -rw-r--r-- 1 christophe christophe 1.1K Jan 4 15:59 server-cert.pem -rw------- 1 christophe christophe 1.7K Jan 4 15:59 server-key.pem drwxr-x--- 2 christophe christophe 4.0K Jan 4 15:59 sys -rw-r----- 1 christophe christophe 12M Jan 4 15:59 undo_001 -rw-r----- 1 christophe christophe 10M Jan 4 15:59 undo_002 ``` Back to your browser and continue the installation of Joomla like we did in the previous chapter. When the installation wizard has finished successfully, return to your Linux console and check if you can see the `configuration.php` file now. ```bash $ pwd /tmp/joomla $ ls site_joomla/configuration.php .rw-r--r-- 2.0k christophe christophe 6.8K Jan 4 15:59 .htaccess $ head -n 10 site_joomla/configuration.php {`Merci de revenir ultérieurement.'; public \$display_offline_message = 1; public \$offline_image = ''; public \$sitename = 'kingsbridge'; public \$editor = 'tinymce'; public \$captcha = '0'; public \$list_limit = 20; `} ``` Yes! This time we have stored your Joomla website on your computer. For the exercise, we can kill your containers by running `docker compose kill` and run `docker compose up --detach` again to start your containers again and, this time, we will retrieve your Joomla site as we just leave it. Nothing is lost now. ```bash $ docker compose kill && docker compose up --detach ``` And since files/folders are now on your computer, you can do everything you want with your website like committing it on a GitHub repository f.i. > **Synchronization has a cost:** > > When using one or more volumes, Docker must constantly check whether the files/folders on your computer are synchronized with the containers. This has a cost in terms of performance. Similarly, if Docker needs to write to a file (or to your Joomla database); if this is only in memory, it will be faster than if you also had to write the database to your disk. However, the difference in speed is not obvious under normal circumstances. If you're a developer writing hundreds or even thousands of records, you'll observe a noticeable difference. ## Start a CLI command When working with a Joomla website, sometimes you need to run some PHP command like `php joomla.php`. To be able to do this, you will need to start an *interactive shell session* in the Joomla container. Did you remember the name of your Joomla service? If no, just open the `compose.yaml` file again. **compose.yaml** ```yaml services: // highlight-next-line joomla: [...] ``` The name of your Joomla service is `joomla` (and `joomladb` is the name of your database service). Now we know everything we need: to run an interactive shell session in the Joomla container, just run `docker compose exec joomla /bin/sh` to *jump* into the Joomla container (*and `docker compose exec joomladb /bin/sh` for the database container*). ```bash $ docker compose exec joomla /bin/sh ``` Let us rewind for a few seconds because it's complicated to understand. On your computer, in your Linux console, if you type `pwd` to get the current folder, you will see `/tmp/joomla` since it's the directory we have used in the tutorial. Then, we jump in the Joomla Docker container by running `docker compose exec joomla /bin/sh` and, you can see it, we don't have any longer a prompt starting with `>` but with `$` but that isn't the only difference. If now, we run `pwd` again, we're no more in your `/tmp/joomla` folder but in `/var/www/html`. It can drive you mad but ... when entering in the Docker container, you're *no more* on your computer, you're virtually in another computer, the container. In that *computer*, the current folder is thus `/var/www/html`. By typing `exit`, you will leave the container, go back to your computer and back in the `/tmp/joomla` folder. ```bash $ pwd /tmp/joomla $ docker compose exec joomla /bin/sh $ pwd /var/www/html $ exit $ pwd /tmp/joomla ``` Something else to try: ```bash $ php --version command not found: php $ docker compose exec joomla /bin/sh $ php --version PHP 8.3.29 (cli) (built: Dec 29 2025 23:29:22) (NTS) Copyright (c) The PHP Group Zend Engine v4.3.29, Copyright (c) Zend Technologies with Zend OPcache v8.3.29, Copyright (c), by Zend Technologies $ exit ``` So, on my computer (and probably on yours), by typing `php --version`, I got an error. That is correct. I don't have PHP on my computer. *I work daily with PHP for years and I don't have it on my machine.* 😄 Then I jump in the Joomla container and type the same command again and, yes, inside the Docker container of Joomla, yes, PHP is installed and here, it's the version 8.3.29. ## Using alias Docker will always start your project on your localhost and on a port like `http://127.0.0.1:`8080. Instead of using the IP address, it would be much nicer to use an alias like `http://`kingsbridge`:`8080. For this, on Windows, edit the file `C:\Windows\System32\Drivers\etc\hosts` and add the kingsbridge line as below illustrated: **C:\Windows\System32\Drivers\etc\hosts** ``` 127.0.0.1 localhost // highlight-next-line 127.0.0.1 kingsbridge ``` Save the file. Now you can surf to `http://`kingsbridge`:`8080 > Screenshot — http\://kingsbridge:8080 > > ![Using the Kingsbridge alias](./images/using_alias.webp) > **Note:** > > When creating an alias in the host file, some people prefer to use the > `.local` suffix like in `127.0.0.1` kingsbridge`.local` to make clear > it's a localhost site. If you prefer this, so the URL will be `http://` > kingsbridge`.local:`8080 ## Using another port Imagine you have another project, not the `Kingsbridge` one this time. Can you have many Docker projects running at the same time? Yes, of course! You just need to make sure to use another, unused, port. Consider the `shiring` project `compose.yaml` file **compose.yaml** ```yaml // highlight-next-line name: shiring services: joomla: image: joomla ports: // highlight-next-line - 8081:80 [...] ``` We will use the port `8081` for that project and, in your host file, we will add `127.0.0.1 shiring`. So, now and at the same time, we can access to `http://`kingsbridge`:`8080 and to `http://shiring:8081` without conflicts since we have used a separate port number. Kingsbridge can be a Joomla 4.4 / PHP 7.4 website while shiring is on Joomla 5.0 / PHP 8.2 without any conflict. **And that's just astonishingly simple, if you think back to the way you worked before Docker.** > **Information:** > > Using another port isn't mandatory. You can have several websites on the same port as 8080. But, in that case, we won't be able to use these websites at the same time. You can start the first one and when trying to start the second one, Docker will say *port already in use*. This isn't a problem; you can just run `docker compose stop` for the first one so free the port then start the second one. > > ```bash > $ docker compose stop > ``` > **Note:** > > In this article, I'm using port 8080 because Joomla has used it in his > default `compose.yaml` file. You're not forced to use that one, you can > perfectly use `80` and not 8080. In my daily work, I'm using ports `80`, > `81`, `82`, `83`, ... varying the second figures for my projects. ## Did you prefer PostgreSQL or MariaDB So far, we've chosen to use MySQL as our database manager. Our `compose.yaml` file is the one, slightly modified, that can be found on . Could you opt for something other than MySQL? Of course, as long as Joomla supports this system (see ). We just need to replace the `joomladb` service, don't use anymore `mysql` but the one we wish. **Using PostgreSQL** Let's try PostgreSQL... We'll replace `mysql`. The official PostgreSQL Docker image can be retrieved on . The documentation tell us which `image` we should use and how to define variables like the default password (`POSTGRES_PASSWORD`). We also need to change a few variables: * The default database user should be specified, and it will be f.i. `postgres`, * We should define too the name of the database in both services; we'll name our db `joomla_db` and * We need to inform Joomla that we'll use PostgreSQL so we need to set `JOOMLA_DB_TYPE` to `pgsql` (can be either `mysql` or `pgsql`). > **Note:** > > This information has been retrieved from this Pull requests: > . Our `compose.yaml` will become: **compose.yaml** ```yaml name: kingsbridge services: joomla: image: joomla container_name: kingsbridge-app restart: always ports: - 8080:80 environment: - JOOMLA_DB_HOST=joomladb // highlight-next-line - JOOMLA_DB_USER=postgres - JOOMLA_DB_PASSWORD=example // highlight-next-line - JOOMLA_DB_NAME=joomla_db // highlight-next-line - JOOMLA_DB_TYPE=pgsql depends_on: - joomladb joomladb: // highlight-next-line image: postgres:16.0-alpine container_name: kingsbridge-db restart: always environment: // highlight-next-line - POSTGRES_DB=joomla_db // highlight-next-line - POSTGRES_USER=postgres // highlight-next-line - POSTGRES_PASSWORD=example ``` Now, during the configuration of Joomla, make sure to choose `PostgreSQL (PDO)` for the database type and fill in the database wizard with the correct values. > **Use alpine images wherever possible:** > > For the first time during this tutorial, we're using an `alpine` image (`postgres:16.0-alpine` here). These images are lighter than a non-alpine image. They contain the bare essentials for running the service, whereas a *traditional* image contains additional tools, binaries, ... that you may need but are not certain to use. **Using MariaDB** If you plan to use MariaDB, here is the Docker Official Image: . **compose.yaml** ```yaml name: kingsbridge services: joomla: image: joomla container_name: kingsbridge-app restart: always ports: - 8080:80 environment: - JOOMLA_DB_HOST=joomladb - JOOMLA_DB_PASSWORD=example depends_on: - joomladb joomladb: // highlight-next-line image: mariadb:11.1.2 container_name: kingsbridge-db restart: always environment: - MYSQL_ROOT_PASSWORD=example ``` For MariaDB, please select `MySQLi` during the installation wizard. ## Extra information ### The container will be restarted by Windows If you shut down your computer, if one or more containers are still running, Docker will run them again on the next startup. This means that the next time you start your computer, your website will still be accessible, and you won't have to run a single command. It's so easy! ### Working with Windows Imagine you're in your console (f.i. run `cd /tmp/joomla` in the Linux console) and you wish to open the site with Visual Studio Code, already installed on your Windows machine. It's easy: just run `code .` to open the current folder so your project within vscode. Another use case, you wish to start the Windows Explorer program and navigate in your project's structure. Here too, it's possible, just run `explorer.exe .` to open it and load the current directory. See my "[Open your Linux folder in Windows Explorer](/blog/wsl-windows-explorer)" article to read more about this feature. ## Bonus - Install Joomla with a CLI one-liner The `compose.yaml` used at the beginning of this article was the minimal one. Here is the **final** version, carrying everything we've added since: a project name, explicit container names, and the two volumes so your site and your database survive a `docker compose down`. Just click on the `Generate install script` below, copy/paste the script in your console, run it and follow the instruction. After a few seconds, you'll have a running Joomla website — one you can actually keep. **Project: /tmp/joomla** > **Note:** > > Now, please run 'UID=$(id -u) GID=$(id -g) docker compose up --detach' to > create your Joomla site. Wait a few and open your browser, surf to > to open your Joomla site. The database type should be > `MySQLi`, the name of the host should be `joomladb`, the username should be > `root` and the password for that user should be `example`. **compose.yaml** ```yaml name: yourprojectname services: joomla: image: joomla:6.0.1-php8.3-apache container_name: kingsbridge-app restart: always ports: - 8080:80 environment: - JOOMLA_DB_HOST=joomladb - JOOMLA_DB_PASSWORD=example depends_on: joomladb: condition: service_healthy user: ${UID:-1000}:${GID:-1000} volumes: - ./site_joomla:/var/www/html joomladb: image: mysql:8.0.13 container_name: kingsbridge-db restart: always environment: - MYSQL_ROOT_PASSWORD=example healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] timeout: 20s retries: 10 user: ${UID:-1000}:${GID:-1000} volumes: - ./db:/var/lib/mysql ``` (create empty directory: site\_joomla) (create empty directory: db) > **Caution:** > > Make sure, for each project, to update the `name:` line and if you plan to be able to run your Joomla sites concurrently, make sure to update the port number for Joomla and chose a new one every time (can be `80`, `81`, `82` and so on). ## Conclusion What I like most about this setup isn't the speed — it's that a Joomla site becomes a *folder*. One `compose.yaml`, two sub-folders, and you can have five of them side by side, each on its own port, each with its own database, none of them able to break the others. Deleting a site is deleting a folder. From here, two natural next steps: [Create your Joomla website using Docker - Part 2](/blog/docker-joomla-part-2) goes further with the day-to-day workflow, and [Using Adminer, pgadmin or phpmyadmin](/blog/docker-adminer-pgadmin-phpmyadmin) shows how to browse the database that was just created for you. --- # Update php.ini when using a Docker image > A step-by-step guide on how to update your php.ini file inside a Docker container using a compose.yaml volume mount. Solve common issues like maximum file upload size. --- ![Update php.ini when using a Docker image](/img/v2/docker_tips.webp) **TL;DR** This article shows how to override `php.ini` settings (e.g. the max upload file size) in a Dockerized PHP site: create a local `php.ini` file, mount it over the container's config file via a `volumes` entry in `compose.yaml`, then `docker compose down && docker compose up --detach` to apply the change. This article aims to answer the following situation: *I'm using a Docker image to run my website and I should modify the php.ini file; how do I do this?* A real-world example is: you've followed my article [Create your Joomla website using Docker](/blog/docker-joomla) and everything is working fine. The website is running, and you wish to upload a big file to your site using the Joomla administration web interface. But, then, you get an error *The selected file cannot be transferred because it is larger than the maximum upload size allowed*. ![Your PHP settings before changes](./images/before.webp) One of the easiest ways to do is to create a `.ini` file on your disk and [share the file with your container](/blog/docker-volume). Most probably, you'll have a `compose.yaml` file, please edit the file. For the illustration, below you'll find a copy of the simplest `compose.yaml` file you can retrieve in my [Create your Joomla website using Docker](/blog/docker-joomla) blog post. *You can have yours of course*. This is just for example. **compose.yaml** ```yaml services: joomla: image: joomla restart: always ports: - 8080:80 environment: - JOOMLA_DB_HOST=joomladb - JOOMLA_DB_PASSWORD=example joomladb: image: mysql:8.0.13 restart: always environment: - MYSQL_ROOT_PASSWORD=example ``` ## Step one - Update your yaml file The solution is to add the `volumes` line if not yet present in your file and, the most important one, the line for *overriding* the `php.ini` file like below: **compose.yaml** ```yaml services: joomla: image: joomla restart: always ports: - 8080:80 environment: - JOOMLA_DB_HOST=joomladb - JOOMLA_DB_PASSWORD=example # highlight-next-line volumes: # highlight-next-line - ./php.ini:/usr/local/etc/php/php.ini joomladb: image: mysql:8.0.13 restart: always environment: - MYSQL_ROOT_PASSWORD=example ``` ## Step two - Create your own php.ini file The second thing to do is to create a file called `php.ini` in the same folder as your `compose.yaml` where you'll define your variables; f.i. **php.ini** ```ini file_uploads = On memory_limit = 500M upload_max_filesize = 500M post_max_size = 500M max_execution_time = 600 ``` So, now, your folder contains at least two files: ```bash $ pwd /tmp/joomla $ ls -alh Permissions Size User Group Date Modified Name drwxr-xr-x - christophe christophe 2023-11-04 09:32 . drwxrwxrwt - christophe christophe 2023-11-04 09:32 .. .rw-r--r-- 325 christophe christophe 2023-11-04 09:32 compose.yaml -rw-r--r-- 1 christophe christophe 2023-12-22 19:51 php.ini ``` ## Step three - Restart your container This done, just run `docker compose down ; docker compose up --detach` in your terminal to stop your current container(s) and restart it/them. Now, when restarting, Docker will take into account your latest changes and update accordingly the `php.ini` file that is present in Docker. ![Your PHP settings after changes](./images/after.webp) --- # Start Joomla with Docker in just a few clicks > Start your Joomla website with Docker in just a few clicks! Follow this quick, right-to-the-point guide to launch your Joomla project instantly using a simple Docker Compose setup. --- ![Start Joomla with Docker in just a few clicks](/img/v2/joomla.webp) **TL;DR** This is a minimal, no-frills guide to launching Joomla with Docker: copy the official `compose.yaml` snippet from Docker Hub, run `docker compose up --detach`, and log into `http://127.0.0.1:8080/administrator` with the default `joomla`/`joomla@secured` credentials defined in that file. Yesterday at lunchtime, while chatting with a friend, he challenged me with a simple request: explain the easiest way in the world to start a Joomla project with Docker. The aim is to hop, hop, copy/paste a file, hop, Joomla is launched and you can start playing with the site. *Three things you'll want soon after: a [volume](/blog/docker-volume) so the site survives a `docker compose down`, [Adminer or phpmyadmin](/blog/docker-adminer-pgadmin-phpmyadmin) to look inside the database, and [Update php.ini when using a Docker image](/blog/docker-php-ini) the day Joomla refuses your file upload as being too large.* Let's take a look; not in detail, but right to the point. **Here are the very few steps to follow to start your Joomla website using Docker:** 1. Surf to 2. Scroll down until you can see the `yaml` content and click on the `Copy` button. That button appears when the mouse pointer is over the text (hidden otherwise) 3. On your computer, create a new file called `compose.yaml` and paste the content there 4. The most difficult part is now: please start a new console, go to the folder where you've just created the file (in my case, I've created the file in my `/tmp/joomla` folder so I jump in it using `cd /tmp/joomla`) 5. Still in your console, please run `docker compose up --detach`. From now, Docker will download (just the first time) the Joomla CMS, PHP, Apache and MySQL. Then Docker will start the downloaded images (called containers). Surf to `http://127.0.0.1:8080/administrator` and enjoy! > Screenshot — http\://127.0.0.1:8080/administrator > > ![Joomla administrator](./images/administrator.webp) > **Caution:** > > The admin account to use is `joomla` and its password is `joomla@secured` (as defined in the yaml file you've just copied earlier). > **Information:** > > This article is deliberately simple; please follow the [Joomla](/blog/tags/joomla) tag if you want more than this introduction. --- # Create your Joomla website using Docker - Part 2 > Create your persistent Joomla website with Docker in one command. This Part 2 guide focuses on advanced automation using compose.yaml and makefile. --- ![Create your Joomla website using Docker - Part 2](/img/v2/joomla.webp) **TL;DR** This guide details how to automate local Joomla development with Docker by combining `compose.yaml`, `.env`, and `makefile` configurations to deploy fully configured sites with a single command. It explains how to bypass the standard installation wizard by pre-defining environment variables and demonstrates how to manage multiple isolated projects with different Joomla and PHP versions simultaneously on one machine. The tutorial provides specific configuration examples to ensure data persistence and enable conflict-free operation across concurrent instances. At the end of 2023, I wrote a very long post about using Joomla with Docker: [Create your Joomla website using Docker - Part 1](/blog/docker-joomla). It was a step-by-step introduction to Docker and the creation of a local Joomla-based website. I explained, at length, the various steps involved in creating a website, choosing a database engine (MySQL, MariaDB or PostgreSQL), choosing a specific version of Joomla / database engine, the type of volumes (should the site and database be kept in RAM or on the hard disk (i.e. the notion of volumes)) and many other things such as choosing the port for the website, creating an alias (something like `http://my_project.local` instead of `http://127.0.0.1`). I strongly urge you to read or re-read this article before starting this one. > **Caution:** > > Here is the link to that article : [Create your Joomla website using Docker - Part 1](/blog/docker-joomla). As Docker and Joomla are evolving rapidly, I propose here to start from where we were and see what has changed since then. We're also going to take automation a step further, making much greater use of the concept of `makefile` and a configuration file called `.env`. **Quick Jump:** [See it in Action](#result) | [Before starting](#before-starting) ## Result ```bash $ make up UID=$(id -u) GID=$(id -g) docker compose up --detach [+] Running 33/12 ✔ joomla Pulled 16.1s ✔ joomladb Pulled 10.7s [+] Running 3/3 ✔ Network runningjoomlaindocker_default Created 0.0s ✔ Container joomla-db Healthy 31.7s ✔ Container joomla-app Started 31.1s ``` That's the whole deployment: Docker pulls Joomla, PHP, Apache and MySQL, wires them together, and a minute later the site is reachable in the browser — no manual database setup, no editing PHP files by hand. > Screenshot — http\://127.0.0.1:8080/installation/index.php > > ![Joomla installer](./images/joomla_installer.webp) > **Hey! did you notice?:** > > We've created our Joomla website by **just running one command**. Don't trust? Open your browser and surf to `http://127.0.0.1:8080`. ## Why it works * `compose.yaml`, `.env` and `makefile` together become a single point of configuration: one file to change the Joomla or PHP version, one for the port number and project name, and `make` targets so nobody has to remember the raw `docker compose` incantations. * `depends_on` + `healthcheck` in `compose.yaml` make Joomla wait for the database to actually be ready before starting its own installation — no crash-on-boot race condition. * Because the whole setup lives in three small files, the same recipe scales to several isolated projects (different Joomla/PHP/MySQL versions) on the same machine, as the *Advanced configuration* and *Starting three different projects* sections below show. ## Create the three configuration files In contrast to article [Create your Joomla website using Docker - Part 1](/blog/docker-joomla), where I began by explaining a few concepts before moving on to discuss the subject, let's do the opposite here: let's start with the three files we are going to use, and then see how they work. Please don't try too hard to understand the contents of the files; we'll come back to that later. Create the three files in a folder on your hard disk for the time being (f.i. in `/tmp/joomla`), so that we can quickly get to the heart of the matter: creating a Joomla site with just one line of commands to run on your computer. ### Our orchestration file: compose.yaml We need a file that will explain to Docker how we want to configure Joomla and, also, the type of database we want. Our orchestration file will also inform Docker that we want the Joomla site to be persistent, i.e. the files to be saved on our hard disk. The same applies to the database. We need to do this so that, if we voluntarily **remove** the Docker container to release memory f.i., we want that the Joomla website is saved on disk. So, next time we will wake **up** our site, files and database tables will be restored. > **Persistence:** > > There are two types of persistence, the one managed natively by Docker (*intern volumes*) or the one where we'll see the files in our project's folder (*mounted volumes*). We'll opt for the latter. See my article [Using volumes with Docker, use cases](/blog/docker-volumes) if you want to learn more. We're also going to define a dependency (based on `depends_on` and `healthcheck`): Joomla must wait for the database server to be ready before starting its installation (in fact, if the Joomla installer tries to access MySQL before the system is even loaded, our project will crash during container loading). As you can see, our `compose.yaml` file is of tremendous importance for the proper definition of our project. > **Note:** > > In recent months, the name of the file to be used has changed from `docker-compose.yaml` to `compose.yaml`; the old name is still supported for now, but might as well use the new one. See [docs.docker.com](https://docs.docker.com/compose/intro/compose-application-model/#the-compose-file) if you want more info about this change. Please create a new folder (f.i. `mkdir /tmp/joomla && cd $_`) on your hard disk and create the `compose.yaml` file with this content: ```bash $ mkdir /tmp/joomla && cd $_ ``` **compose.yaml** ```yaml name: ${PROJECT_NAME:-your-project-name} services: joomla: image: joomla:${JOOMLA_VERSION:-latest} container_name: ${CONTAINER_PREFIX:-joomla}-app restart: always ports: - ${WEB_PORT:-8080}:80 environment: - JOOMLA_DB_HOST=joomladb - JOOMLA_DB_NAME=${DB_NAME:-joomla} - JOOMLA_DB_PASSWORD=${DB_PASSWORD:-examplepass} - JOOMLA_DB_USER=${DB_USER:-joomla} depends_on: joomladb: condition: service_healthy user: ${UID:-1000}:${GID:-1000} volumes: - ./joomla_data:/var/www/html networks: - joomla_network joomladb: image: mysql:${MYSQL_VERSION:-latest} container_name: ${CONTAINER_PREFIX:-joomla}-db restart: always ports: - ${MYSQL_PORT-:3306}:3306 environment: - MYSQL_DATABASE=${DB_NAME:-joomla} - MYSQL_PASSWORD=${DB_PASSWORD:-examplepass} - MYSQL_RANDOM_ROOT_PASSWORD='1' - MYSQL_USER=${DB_USER:-joomla} healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] timeout: 20s retries: 10 user: ${UID:-1000}:${GID:-1000} volumes: - ./db_data:/var/lib/mysql networks: - joomla_network networks: joomla_network: ``` ### We'll use a configuration file called .env We're also going to use a configuration file named `.env` (which is never anything more than a silly dictionary of keys - values) to differentiate our projects. Please create a second file called `.env` with this content: **.env** ``` CONTAINER_PREFIX=joomla DB_NAME=joomla DB_PASSWORD=examplepass DB_USER=joomla JOOMLA_VERSION=5.2.0-php8.3-apache MYSQL_PORT=3306 MYSQL_VERSION=8.4.2 PROJECT_NAME=running-joomla-in-docker WEB_PORT=8080 ``` Using a `.env` file is really useful so you no longer need to make changes to the `compose.yaml` file, which can stay standard across all your projects. If you need two or more Joomla websites (you're a web agency starting to use Docker for your development), you'll be able to only do changes to the `.env` file by updating the `WEB_PORT` (each project should have his unique port) and, you'll probably update the `PROJECT_NAME` too to f.i. your customer name. > **Information:** > > `WEB_PORT`, `PROJECT_NAME`, ... are just *variables*. If you look back to your just copied/pasted `compose.yaml` content, you'll retrieve these variables like on the line `image: joomla:${JOOMLA_VERSION:-latest}`. > > The `${XXX:-yyy}` syntax means: if the variable exists, use it (the `XXX` part) otherwise use a default value (the `yyy` part). > > So, here, if `JOOMLA_VERSION` exists and this is the case in our `.env` file, then use his value. We'll then use `image: joomla:5.2.0-php8.3-apache`. If `JOOMLA_VERSION` was missing in our `.env`, then we'll use `latest` and thus `image: joomla:latest`. ### Let's make our lives simpler and lazier; using a makefile The third and last file we'll use is being called `makefile` and that file will contain some *scripts* i.e. we'll create a script called `up` for building our website. To be able to use a `makefile`, you should have `GNU Make` binary installed on your machine. > **Caution:** > > If you don't know if you already have `GNU make`, just run `which make` in the console. If you see `make not found` then please run `sudo apt-get update && sudo apt-get install make` to complete the installation. > > ```bash > $ which make > ``` > > ```bash > $ sudo apt-get update && sudo apt-get install make > ``` So, please create a file called `makefile` in your current directory with this content: **makefile** ``` include .env config: @UID=$$(id -u) GID=$$(id -g) docker compose config down: stop -@UID=$$(id -u) GID=$$(id -g) docker compose down log: -@UID=$$(id -u) GID=$$(id -g) docker compose logs reset: down -@rm -rf db_data joomla_data start: @clear @printf "\033[1;33m%s\033[0m\n\n" "To start your site, please jump to http://127.0.0.1:${WEB_PORT}" @printf "\033[1;33m%s\033[0m\n\n" "Go to http://127.0.0.1:${WEB_PORT}/administrator to open your backend." @printf "\033[1;104m%s\033[0m\n\n" "Below a summary of your current installation:" @printf "\033[1;34m%s\033[0m\n\n" "JOOMLA" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Project name" "${PROJECT_NAME}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Version" "${JOOMLA_VERSION}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n\n" " * Port" "${WEB_PORT}" @printf "\033[1;34m%s\033[0m\n\n" "DATABASE" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Host" "joomla-db" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * User name" "${DB_USER}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Password" "${DB_PASSWORD}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Database name" "${DB_NAME}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Version" "${MYSQL_VERSION}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n\n" " * Port" "${MYSQL_PORT}" stop: -@UID=$$(id -u) GID=$$(id -g) docker compose stop up: -@mkdir -p db_data joomla_data @UID=$$(id -u) GID=$$(id -g) docker compose up --detach --build ``` > **Indentation should be TAB not spaces:** > > It's terribly vicious because it's totally silent: the indentation in a > makefile must be tabs, not spaces. When you copy/paste the above code to > create your file, make sure your text editor uses tabs. If it doesn't, the > commands defined in the file won't work. > **Note:** > > If you want to learn more about GNU Make, check my [Linux Makefile - When to use a makefile](/blog/makefile-using-make) article ## Before starting We're ready. Just before you begin, please check that you have the three files below (please run `ls -alh`): **Project: /tmp/joomla** > **Note:** > > Now, please run 'UID=$(id -u) GID=$(id -g) docker compose up --detach' to > create your Joomla site. Wait a few and open your browser, surf to > to open your Joomla site. The database type should be > `MySQLi`, the name of the host should be `joomladb`, the username should be > `root` and the password for that user should be `example`. **.env** ``` CONTAINER_PREFIX=joomla DB_NAME=joomla DB_PASSWORD=examplepass DB_USER=joomla JOOMLA_VERSION=5.2.0-php8.3-apache MYSQL_PORT=3306 MYSQL_VERSION=8.4.2 PROJECT_NAME=running-joomla-in-docker WEB_PORT=8080 ``` **compose.yaml** ```yaml name: ${PROJECT_NAME:-your-project-name} services: joomla: image: joomla:${JOOMLA_VERSION:-latest} container_name: ${CONTAINER_PREFIX:-joomla}-app restart: always ports: - ${WEB_PORT:-8080}:80 environment: - JOOMLA_DB_HOST=joomladb - JOOMLA_DB_NAME=${DB_NAME:-joomla} - JOOMLA_DB_PASSWORD=${DB_PASSWORD:-examplepass} - JOOMLA_DB_USER=${DB_USER:-joomla} depends_on: joomladb: condition: service_healthy user: ${UID:-1000}:${GID:-1000} volumes: - ./joomla_data:/var/www/html networks: - joomla_network joomladb: image: mysql:${MYSQL_VERSION:-latest} container_name: ${CONTAINER_PREFIX:-joomla}-db restart: always ports: - ${MYSQL_PORT-:3306}:3306 environment: - MYSQL_DATABASE=${DB_NAME:-joomla} - MYSQL_PASSWORD=${DB_PASSWORD:-examplepass} - MYSQL_RANDOM_ROOT_PASSWORD='1' - MYSQL_USER=${DB_USER:-joomla} healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] timeout: 20s retries: 10 user: ${UID:-1000}:${GID:-1000} volumes: - ./db_data:/var/lib/mysql networks: - joomla_network networks: joomla_network: ``` **makefile** ``` include .env config: @UID=$$(id -u) GID=$$(id -g) docker compose config down: stop -@UID=$$(id -u) GID=$$(id -g) docker compose down log: -@UID=$$(id -u) GID=$$(id -g) docker compose logs reset: down -@rm -rf db_data joomla_data start: @clear @printf "\033[1;33m%s\033[0m\n\n" "To start your site, please jump to http://127.0.0.1:${WEB_PORT}" @printf "\033[1;33m%s\033[0m\n\n" "Go to http://127.0.0.1:${WEB_PORT}/administrator to open your backend." @printf "\033[1;104m%s\033[0m\n\n" "Below a summary of your current installation:" @printf "\033[1;34m%s\033[0m\n\n" "JOOMLA" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Project name" "${PROJECT_NAME}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Version" "${JOOMLA_VERSION}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n\n" " * Port" "${WEB_PORT}" @printf "\033[1;34m%s\033[0m\n\n" "DATABASE" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Host" "joomla-db" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * User name" "${DB_USER}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Password" "${DB_PASSWORD}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Database name" "${DB_NAME}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Version" "${MYSQL_VERSION}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n\n" " * Port" "${MYSQL_PORT}" stop: -@UID=$$(id -u) GID=$$(id -g) docker compose stop up: -@mkdir -p db_data joomla_data @UID=$$(id -u) GID=$$(id -g) docker compose up --detach --build ``` ```bash $ ls -alh total 20K drwxr-xr-x 2 christophe christophe 4.0K Jan 4 16:10 . drwxrwxrwt 13 root root 4.0K Jan 4 16:10 .. -rw-r--r-- 1 christophe christophe 201 Jan 4 16:10 .env -rw-r--r-- 1 christophe christophe 1.2K Jan 4 16:10 compose.yaml -rw-r--r-- 1 christophe christophe 1.6K Jan 4 16:10 makefile ``` ### Check our configuration By running `make config` (`config` is an action we've defined in our `makefile`), we can display the configuration of our project on the screen *(just display, nothing is fired)*. ```bash $ make config ``` We should see a lot of technical information starting with something like: ```yaml name: running-joomla-in-docker services: joomla: container_name: joomla-app depends_on: joomladb: condition: service_healthy required: true environment: JOOMLA_DB_HOST: joomladb JOOMLA_DB_NAME: joomla JOOMLA_DB_PASSWORD: examplepass JOOMLA_DB_USER: joomla image: joomla:5.2.0-php8.3-apache networks: joomla_network: null ports: - mode: ingress target: 80 published: "8080" protocol: tcp restart: always user: 1002:1002 volumes: - type: bind source: /tmp/joomla/joomla_data target: /var/www/html bind: create_host_path: true joomladb: container_name: joomla-db environment: MYSQL_DATABASE: joomla MYSQL_PASSWORD: examplepass MYSQL_RANDOM_ROOT_PASSWORD: "'1'" MYSQL_USER: joomla healthcheck: test: - CMD - mysqladmin - ping - -h - localhost timeout: 20s retries: 10 image: mysql:8.4.2 networks: joomla_network: null ports: - mode: ingress target: 3306 published: "3306" protocol: tcp restart: always user: 1002:1002 volumes: - type: bind source: /tmp/joomla/db_data target: /var/lib/mysql bind: create_host_path: true networks: joomla_network: name: running-joomla-in-docker_joomla_network ``` > **Information:** > > The output here above is a **partial content** and just show the first lines displayed to the console by the `make config` command. The objective of `make config` is to make sure that all files are correct (otherwise we'll get error messages) and that variables are well replaced by their values. You can see f.i. that the Joomla version is well the one we've specified in the `.env` file and the user line (the last line here above) is correctly initialised to the **user\_id:group\_id** of your current user. > **UID/GID:** > > The **user\_id:group\_id** will vary depending on your own configuration. It'll be `1000:1000` in the most situations, but this can be something else when, f.i., you've defined more than one user in your Linux distribution; the first user probably be `1000:1000`, the second `1001:1001` and so on. ## Let's wake up Joomla... Here we are at the heart of the subject: we would like to see a beautiful, fresh Joomla site. Is it possible to do this in a single command and even without having to install anything by hand? The answer is "Yes" and `make up` will do the magic. In your console, just run `make up`, and please wait one minute. The very first time, you'll get the console output shown at the top of this article. What does that mean? Docker has retrieved (*pulled*) from the Internet our `joomla` and `joomladb` services (in other terms, has downloaded the Joomla CMS (including PHP and Apache) and MySQL). By running then command below (or just `docker container list` to get all the columns), you'll get the list of running containers: ```bash $ {`docker container list --all --format "table {{.Image}}\\t{{.Names}}\\t{{.Status}}\\t{{.Ports}}"`} IMAGE NAMES STATUS PORTS joomla:5.2.0-php8.3-apache joomla-app Up 6 minutes 0.0.0.0:8080->80/tcp mysql:8.4.2 joomla-db Up 7 minutes (healthy) 3306/tcp, 33060/tcp ``` As we can see, yay!, we have our two services (named containers) which are Joomla and MySQL, and they are both *Up* (see the `Status` column). We see that Joomla uses port `8080` and MySQL uses port `3306`. > **Information:** > > The port `8080` is the one we've specified in our `.env` (variable `WEB_PORT`) and, the same for MySQL, it uses port `3006` because that's the one we defined (`MYSQL_PORT`). > > Note that `3306` is the standard port for MySQL. ### ... or let's ask him to fall asleep (keep containers) The opposite command to `make up` is `make stop`. The `stop` action will stop our Joomla and MySQL containers (read `make them sleeping`) but without removing files (your site and your database) from your hard drive. By running `make up` again, you'll retrieve them. `up` and `stop` are like a light switch. ```bash $ make stop ``` ### ... or let's ask him to remove containers (free more memory) The `make down` command will stop the containers as well remove them. But in our case, we'll still have files on our hard disk so we'll not lose our website. ```bash $ make down ``` ### ... or let's ask him to suicide (remove containers and files) The `make reset` command will stop containers but, too, remove files on the disk. ```bash $ make reset ``` > **Danger:** > > By running `make reset`, you'll lose everything and be ready to create a new fresh website. ## Time to starting the website Once `make up` has been fired and your containers in a `Up` state, our Joomla site is ready. ```bash $ make up ``` You will need some information like credentials for the database; this is why the `make start` screen was defined. Please run `make start` in console. You'll get a screen like the one below: ```bash $ make start ``` ```none To start your site, please jump to http://127.0.0.1:8080 Go to http://127.0.0.1:8080/administrator to open your backend. Below a summary of your current installation: JOOMLA * Project name running-joomla-in-docker * Version latest * Port 8080 DATABASE * Host joomla-db * User name joomla * Password examplepass * Database name joomla * Version 8.4.2 * Port 3306 ``` As you can see, the port number is `8080`; the one we've specified in our `.env` file. Simply CTRL click on the URL if your terminal support this option (otherwise, manually start your preferred web browser and navigate to the specified URL) — you'll land on the installer screen shown at the top of this article. **Congratulations, you have successfully installed a fresh Joomla website using Docker!** You'll need to configure your new fresh site. In the **Database Configuration** screen, you'll need to fill in the **Host**, the **username**, the **password** and the **database name**. This information is displayed on the console; thanks `make start`. Just use these values when prompted. ## Time for a break **What have we seen so far?** * Without having anything other than Docker you can install Joomla and MySQL from the command line, in the version you want; * That with an `.env` file we can vary certain values to allow us to have several projects on our hard drive (several Joomla sites); * That with the `compose.yaml` file we tell Docker what to do to make Joomla and MySQL talk to each other, and * The `compose.yaml` file is valuable since it allows you to define a number of configuration variables. Let's go a little further. ## Advanced configuration for Joomla The official Joomla Docker image is maintained by the Joomla community on Docker Hub: . You'll retrieve there a list of *tags* i.e. a list of versions you can install using Docker but you'll also find a **How to use this image** section and that one is really useful. Below the list of variables supported by Joomla by the end of September 2024: | Variable name | Description | | ------------------------- | ------------------------------------------------------------------------------------------------------- | | `JOOMLA_DB_HOST` | Defaults to the IP and port of the linked mysql container | | `JOOMLA_DB_USER` | Defaults to `root` | | `JOOMLA_DB_PASSWORD` | Defaults to the value of the `MYSQL_ROOT_PASSWORD` environment variable from the linked mysql container | | `JOOMLA_DB_PASSWORD_FILE` | Path to a file containing the database password | | `JOOMLA_DB_NAME` | Defaults to `joomla` | | `JOOMLA_DB_TYPE` | Defaults to `mysqli`; options: `mysqli`, `pgsql` | The following environment variables are also recognized for configuring auto deployment (i.e. skip the browser setup) for your Joomla instance: | Variable name | Description | | ------------------------- | ------------------------------------------------------------------------ | | `JOOMLA_SITE_NAME` | Name of the Joomla site | | `JOOMLA_ADMIN_USER` | Full name of the Joomla administrator | | `JOOMLA_ADMIN_USERNAME` | Username of the Joomla administrator | | `JOOMLA_ADMIN_PASSWORD` | Password of the Joomla administrator | | `JOOMLA_ADMIN_EMAIL` | Email address of the Joomla administrator | | `JOOMLA_EXTENSIONS_URLS` | Semicolon-separated list of URLs to install Joomla extensions from | | `JOOMLA_EXTENSIONS_PATHS` | Semicolon-separated list of file paths to install Joomla extensions from | | `JOOMLA_SMTP_HOST` | SMTP host for outgoing email | | `JOOMLA_SMTP_HOST_PORT` | SMTP port for outgoing email | Pay attention to the last table here above. > **Pretty cool!:** > > We could therefore completely ignore the Joomla configuration screens!!! By specifying certain values, we could therefore install a beautiful, fresh Joomla and already display a functional website. Let's try. We'll update our three files to skip the installation process by predefining values like the name and the password for our administrator. ### Updated compose.yaml We'll edit our `compose.yaml` file and add new entries: **compose.yaml** ```yaml name: ${PROJECT_NAME:-your-project-name} services: joomla: image: joomla:${JOOMLA_VERSION:-latest} container_name: ${CONTAINER_PREFIX:-joomla}-app restart: always ports: - ${WEB_PORT:-8080}:80 environment: - JOOMLA_DB_HOST=joomladb - JOOMLA_DB_NAME=${DB_NAME:-joomla} - JOOMLA_DB_PASSWORD=${DB_PASSWORD:-examplepass} - JOOMLA_DB_USER=${DB_USER:-joomla} # highlight-next-line - JOOMLA_SITE_NAME=${JOOMLA_SITE_NAME:-Joomla} # highlight-next-line - JOOMLA_ADMIN_USER=${JOOMLA_ADMIN_USER:-Joomla Hero} # highlight-next-line - JOOMLA_ADMIN_USERNAME=${JOOMLA_ADMIN_USERNAME:-joomla} # highlight-next-line - JOOMLA_ADMIN_PASSWORD=${JOOMLA_ADMIN_PASSWORD:-joomla@secured} # highlight-next-line - JOOMLA_ADMIN_EMAIL=${JOOMLA_ADMIN_EMAIL:-joomla@example.com} depends_on: joomladb: condition: service_healthy user: ${UID:-1000}:${GID:-1000} volumes: - ./joomla_data:/var/www/html networks: - joomla_network joomladb: image: mysql:${MYSQL_VERSION:-latest} container_name: ${CONTAINER_PREFIX:-joomla}-db restart: always ports: - ${MYSQL_PORT-:3306}:3306 environment: - MYSQL_DATABASE=${DB_NAME:-joomla} - MYSQL_PASSWORD=${DB_PASSWORD:-examplepass} - MYSQL_RANDOM_ROOT_PASSWORD='1' - MYSQL_USER=${DB_USER:-joomla} healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] timeout: 20s retries: 10 user: ${UID:-1000}:${GID:-1000} volumes: - ./db_data:/var/lib/mysql networks: - joomla_network networks: joomla_network: ``` ### Updated .env And we'll also update our `.env` file like this: **.env** ``` CONTAINER_PREFIX=joomla DB_NAME=joomla DB_PASSWORD=examplepass DB_USER=joomla // highlight-next-line JOOMLA_ADMIN_EMAIL=joomla@example.com // highlight-next-line JOOMLA_ADMIN_PASSWORD=joomla@secured // highlight-next-line JOOMLA_ADMIN_USER=Joomla Hero // highlight-next-line JOOMLA_ADMIN_USERNAME=joomla // highlight-next-line JOOMLA_SITE_NAME=Joomla JOOMLA_VERSION=5.2.0-php8.3-apache MYSQL_PORT=3306 MYSQL_VERSION=8.4.2 PROJECT_NAME=running-joomla-in-docker WEB_PORT=8080 ``` ### Updated makefile And, finally, also update the `makefile` like this: **makefile** ``` -include .env config: @UID=$$(id -u) GID=$$(id -g) docker compose config down: stop -@UID=$$(id -u) GID=$$(id -g) docker compose down log: -@UID=$$(id -u) GID=$$(id -g) docker compose logs reset: down -@rm -rf db_data joomla_data start: @clear @printf "\033[1;33m%s\033[0m\n\n" "To start your site, please jump to http://127.0.0.1:${WEB_PORT}" @printf "\033[1;33m%s\033[0m\n\n" "Go to http://127.0.0.1:${WEB_PORT}/administrator to open your backend." @printf "\033[1;104m%s\033[0m\n\n" "Below a summary of your current installation:" @printf "\033[1;34m%s\033[0m\n\n" "JOOMLA" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Project name" "${PROJECT_NAME}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Version" "${JOOMLA_VERSION}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Port" "${WEB_PORT}" # highlight-next-line @printf "\n\033[1;34m%s\033[0m\n\n" " Administration" # highlight-next-line @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Site name" "${JOOMLA_SITE_NAME}" # highlight-next-line @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Admin friendly username" "${JOOMLA_ADMIN_USER}" # highlight-next-line @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Admin username" "${JOOMLA_ADMIN_USERNAME}" # highlight-next-line @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Admin password" "${JOOMLA_ADMIN_PASSWORD}" # highlight-next-line @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n\n" " * Admin email" "${JOOMLA_ADMIN_EMAIL}" @printf "\033[1;34m%s\033[0m\n\n" "DATABASE" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Host" "joomla-db" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * User name" "${DB_USER}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Password" "${DB_PASSWORD}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Database name" "${DB_NAME}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Version" "${MYSQL_VERSION}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n\n" " * Port" "${MYSQL_PORT}" stop: -@UID=$$(id -u) GID=$$(id -g) docker compose stop up: -@mkdir -p db_data joomla_data @UID=$$(id -u) GID=$$(id -g) docker compose up --detach --build ``` As you can see, we've configured some `JOOMLA_ADMIN_xxx` keys and set `JOOMLA_SITE_NAME` to *Joomla*; the name of our website. ### Running the website and skip the installation process Once you've updated the three files; just run `make reset` (to remove our previous site if any) then run `make up` to create a new one. Finally, run `make start` to show a list of important variables: ```bash $ make reset && make up && make start ``` ```none To start your site, please jump to http://127.0.0.1:8080 Go to http://127.0.0.1:8080/administrator to open your backend. Below a summary of your current installation: JOOMLA * Project name running-joomla-in-docker * Version 5.2.0-php8.3-apache * Port 8080 Administration * Site name Joomla * Admin friendly username Joomla Hero * Admin username joomla * Admin password joomla@secured * Admin email joomla@example.com DATABASE * Host joomla-db * Version 8.4.2 * Name joomla * User joomla * Password examplepass * Port 3306 ``` Now, by going to `http://127.0.0.1:8080/administrator`, you'll no longer see the installation screens but, immediately, the login screen. The username to use is `joomla` and its password is `joomla@secured` (as you can see on the console). Your Joomla site is already configured; nice no? ## Time for a second break Ok, so right now, we've seen how to create a fresh Joomla website in seconds with just the `make up` command and how to bypass the installation screens by already providing useful information in our `.env` file. Let's try a real-world example: we are a small web agency and we wish to use Docker for our next projects and we already have three projects in our mind. Or, you're an individual having several websites; all with different Joomla versions. ## Starting three different projects Please create a structure like below i.e. `cd /tmp/joomla && mkdir project_1 project_2 project_3`. ```bash $ cd /tmp/joomla && mkdir project_1 project_2 project_3 ``` Then go back to the *Updated compose.yaml* chapter and create the `compose.yaml` file in both the three projects' folder. Do the same for the `.env` and the `makefile`. The same three files in the three projects folders. At the end, you should have this structure in your `/tmp/joomla` folder: ```tree expanded=true showJSX=false debug=false title="/tmp/joomla" ├── project_1 │   ├── .env │   ├── compose.yaml │   └── makefile ├── project_2 │   ├── .env │   ├── compose.yaml │   └── makefile └── project_3    ├── .env ├── compose.yaml └── makefile ``` One directory by project and, again, our three files. > **Note:** > > The `tree` command isn't native on Linux. If you wish to install it, just run `sudo apt-get update && sudo apt-get install tree`. > > ```bash > $ sudo apt-get update && sudo apt-get install tree > ``` ### Project 1 - Joomla 5.2 > **Project 1 will be a Joomla 5.2:** > > In a very few words: we'll install Joomla 5.2 and MySQL 8.4.2. Please configure the `project_1/.env` file with the following content. As you can see (highlighted lines), that file is the same that the latest `.env` we've used earlier but: * We'll set the `CONTAINER_PREFIX` to an unique value; in our example `joomla_5`, * We need to do the same (i.e. define an unique value) for the MySQL port number; we can use the standard port `3306` for our first project but, let's use `3301` (`1` because it's our first project) * You've understood; we'll do the same for `JOOMLA_SITE_NAME` and `PROJECT_NAME`; remain humble and opt for `Joomla_5` and `joomla5` and * Finally, we need to define a web port number; we'll use `8081` (so our site will be available on `http://127.0.0.1:8081`). Here is the `.env` file for **our first** project: **project\_1/.env** ``` // highlight-next-line CONTAINER_PREFIX=joomla_5 DB_NAME=joomla DB_PASSWORD=examplepass DB_USER=joomla JOOMLA_ADMIN_EMAIL=joomla@example.com JOOMLA_ADMIN_PASSWORD=joomla@secured JOOMLA_ADMIN_USER=Joomla Hero JOOMLA_ADMIN_USERNAME=joomla // highlight-next-line JOOMLA_SITE_NAME=Joomla_5 // highlight-next-line JOOMLA_VERSION=5.2.0-php8.3-apache // highlight-next-line MYSQL_PORT=3301 // highlight-next-line MYSQL_VERSION=8.4.2 // highlight-next-line PROJECT_NAME=joomla5 // highlight-next-line WEB_PORT=8081 ``` Go to your console and run `cd project_1` to jump in the first project's folder then `make up` to create Docker containers for project 1. By going to `http://127.0.0.1:8081/administrator/` we've our first admin page (as you can see, the site is well called **Joomla\_5** as expected). > Screenshot — http\://127.0.0.1:8081/administrator/ > > ![Our first site is running under Joomla 5.2](./images/project_1.webp) > **Caution:** > > Please really be careful to specify the port number of our project. For project 1, the port number is `8081`. As, in fact, we wished by setting the variable `WEB_PORT`. > > So, to access to the first project, our URL is well `http://127.0.0.1:8081`. ### Project 2 - Joomla 4.0 > **Project 2 will be a Joomla 4.0:** > > In a very few words: we'll install Joomla 4.0 and MySQL 5.7.37. Please configure the `project_2/.env` with the following content. As you can see (highlighted lines), that file is the same as the latest `.env` we've used earlier but, just, we'll adjust the name of the project and the web port (so our site will be available on `http://127.0.0.1:8082`). Do the same changes as for project 1 but just replace `1` by `2`: **project\_2/.env** ``` // highlight-next-line CONTAINER_PREFIX=joomla_4 DB_NAME=joomla DB_PASSWORD=examplepass DB_USER=joomla JOOMLA_ADMIN_EMAIL=joomla@example.com JOOMLA_ADMIN_PASSWORD=joomla@secured JOOMLA_ADMIN_USER=Joomla Hero JOOMLA_ADMIN_USERNAME=joomla // highlight-next-line JOOMLA_SITE_NAME=Joomla_4 // highlight-next-line JOOMLA_VERSION=4.0.2-apache // highlight-next-line MYSQL_PORT=3302 // highlight-next-line MYSQL_VERSION=5.7.37 // highlight-next-line PROJECT_NAME=joomla4 // highlight-next-line WEB_PORT=8082 ``` Save the file and run `make up` to start the second website. Go to `http://127.0.0.1:8082/administrator` and yes, we've our second site running on our machine. ```bash $ make up ``` > Screenshot — http\://127.0.0.1:8082/administrator > > ![Our two Joomla projects are running](./images/project_1_2.webp) > **Two Joomla sites having two different PHP and MySQL versions and ... no conflict!:** > > Did you see? We've two websites running on our computer. The first one is a Joomla 5.2 and the second one is Joomla 4.0. > > If you're still awake after this long blog post, to get access to our second website, we need to go to `http://127.0.0.1:8082`. ### Project 3 - Joomla 3.5 > **Project 3 will be a Joomla 3.5:** > > And now, you need to do some maintenance on an old J3.5 website. Let's create one on our computer. Please now configure the `project_3/.env` with the following content. This time, we'll use `3` as suffixes: **project\_3/.env** ``` // highlight-next-line CONTAINER_PREFIX=joomla_3 DB_NAME=joomla DB_PASSWORD=examplepass DB_USER=joomla JOOMLA_ADMIN_EMAIL=joomla@example.com JOOMLA_ADMIN_PASSWORD=joomla@secured JOOMLA_ADMIN_USER=Joomla Hero JOOMLA_ADMIN_USERNAME=joomla // highlight-next-line JOOMLA_SITE_NAME=Joomla_3 // highlight-next-line JOOMLA_VERSION=3.5.1-apache // highlight-next-line MYSQL_PORT=3303 // highlight-next-line MYSQL_VERSION=5.7.31 // highlight-next-line PROJECT_NAME=joomla3 // highlight-next-line WEB_PORT=8083 ``` Save the file and run `make up && make start` to start the third website. Go to `http://127.0.0.1:8083/administrator` and yes, we've our third site running on our machine. ```bash $ make up && make start ``` > Screenshot — http\://127.0.0.1:8083/administrator > > ![Our three Joomla projects are active at the same time on our development machine](./images/project_1_2_3.webp) > **Just wow no?:** > > Three different Joomla versions, MySQL and too PHP and no conflict at all! > > In fact, nothing surprising, it's normal with Docker: everything is isolated in its own container. 😊 ### Start all three projects in just one command If I'm located in my `/tmp/joomla` folder; I can start my three projects at once like this: ```bash $ cd project_1 && make up && cd ../project_2 && make up && cd ../project_3 && make up ``` Here is something you can get: ```bash $ cd project_1 $ make up [+] Running 3/23 ✔ Network joomla_5_network Created 0.0s ✔ Container joomla_5-db Healthy 11.5s ✔ Container joomla_5-app Started 0.4s $ cd ../project_2 $ make up [+] Running 3/3 ✔ Network joomla_4_network Created 0.0s ✔ Container joomla_4-db Healthy 31.0s ✔ Container joomla_4-app Started 31.4s $ cd ../project_3 $ make up [+] Running 3/3 ✔ Network joomla_3_network Created 0.0s ✔ Container joomla_3-db Healthy 62.7s ✔ Container joomla_3-app Started 61.5s ``` ### Other projects? How many sites can I have? As we've just seen here, we can launch several Joomla projects at the same time on our machine and access them as easily as possible by varying the web port number (`8081`, `8082`, `8083`, ...). But how many can I have? Well, that just depends on your machine. For information, here's what I'm seeing right now on my Windows computer (I'm using Docker Desktop and WSL2). ![Docker desktop](./images/docker_desktop.webp) As you can see, I actually have four projects: my current blog (running on Docker too) and the three Joomla projects. If you look at the **CPU (%)** column, you can see that the projects are almost asleep. And this is normal: once the web page has been displayed, Joomla and MySQL don't require CPU anymore. The page is displayed so they are sleeping. For the memory, I've allocated max. 20GB to WSL/Docker and right now, my four projects (including my blog) are eating 3.3 GB combined. I can thus start a lot more projects. But do I need to be able to work on all three projects at the same time? Probably not. So why not stop, f.i., the first and the third project? In the console, just run `make stop` in folder project 1 and 3 by, running `( cd project_1 ; make stop ; cd ../project_3 ; make stop)`. We'll thus stop (not remove; just freeze and release memory) our first and third project. ```bash $ ( cd project_1 ; make stop ; cd ../project_3 ; make stop ) ``` By looking back to Docker Desktop, we can indeed see we've earned 1GB RAM. ![The first and the third projects are released](./images/docker_desktop_stopped.webp) > **Note:** > > In our example here, we've just kept the second project so we can still work on `http://127.0.0.1:8082/` but the two other projects (`http://127.0.0.1:8081/` and `http://127.0.0.1:8083/`) won't respond anymore. We've stopped them. > > Just run `make up` again in f.i. folder `project_1` to make the site back to life. Or, if you prefer the GUI, in Docker Desktop, just click on the `Start` button: > > ```bash > $ ( cd project_1 && make up ) > ``` > > ![Starting a container in Docker Desktop](./images/docker_desktop_start.webp) ## Under the hood: Docker logs *Optional — skip this section if everything is working and you don't need to debug.* Sometimes, if something goes wrong, it's nice to have access to the Docker logs. Simply run `make log` to achieve this. ```bash $ make log ``` Logs will look like this: ```log joomla-db | 2024-10-13 13:30:25+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.4.2-1.el9 started. joomla-db | '/var/lib/mysql/mysql.sock' -> '/var/run/mysqld/mysqld.sock' joomla-db | 2024-10-13T13:30:26.058140Z 0 [System] [MY-015015] [Server] MySQL Server - start. joomla-db | 2024-10-13T13:30:26.238267Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.4.2) starting as process 1 joomla-db | 2024-10-13T13:30:26.239450Z 0 [Warning] [MY-010122] [Server] One can only use the --user switch if running as root joomla-db | 2024-10-13T13:30:26.246482Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started. joomla-db | 2024-10-13T13:30:26.568703Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended. joomla-db | 2024-10-13T13:30:26.852192Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed. [...] joomla-db | 2024-10-13T13:30:26.852232Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now joomla-app | ======================================================================== joomla-app | joomla-app | joomla-app | Ensuring Joomla database is present joomla-app | joomla-app | MySQL Database Created joomla-app | This server is now configured to run Joomla! joomla-app | joomla-app | NOTE: You will need your database server address, database name, joomla-app | and database user credentials to install Joomla. joomla-app | joomla-app | ======================================================================== joomla-app | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.19.0.3. Set the 'ServerName' directive globally to suppress this message [...] joomla-app | [Fri Oct 04 13:31:46.880743 2024] [mpm_prefork:notice] [pid 1:tid 1] AH00163: Apache/2.4.62 (Debian) PHP/8.3.12 configured -- resuming normal operations joomla-app | [Fri Oct 04 13:31:46.880782 2024] [core:notice] [pid 1:tid 1] AH00094: Command line: 'apache2 -D FOREGROUND' joomla-app | 172.19.0.1 - - [04/Oct/2024:06:57:36 +0000] "POST /installation/index.php HTTP/1.1" 200 6497 "http://127.0.0.1:8080/installation/index.php" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0" ``` ## Conclusion Three small files — `compose.yaml`, `.env`, `makefile` — and `make up` turns into your entire Joomla deployment story: fresh installs, pre-configured sites that skip the installer wizard, and as many isolated projects (different Joomla/PHP/MySQL versions) as your machine can carry, each one started, stopped or torn down with a single command. At this point, you might be wondering: this is all great, but I already have a Joomla site and I'd like to take a backup of my site and restore it locally using Docker; how can I do this? If you're indeed wondering, read the next article: [Restore a Joomla backup using Docker](/blog/docker-joomla-restore-jpa) --- # Restore a Joomla backup using Docker > Easily restore your Joomla website backup locally using Docker, Docker Compose, and Akeeba Kickstart. A simple make import command is all it takes. --- ![Restore a Joomla backup using Docker](/img/v2/joomla.webp) **TL;DR** This guide explains how to restore a Joomla site locally by combining Docker, an Akeeba Backup archive, and the Akeeba Kickstart utility. Users configure a custom environment with PHP and Apache, then execute a simple import command to deploy the necessary files and containers. The process is completed by accessing the Kickstart wizard in a browser to finalize the database connection and site restoration. In previous articles ([Part 1](/blog/docker-joomla) and [Part 2](/blog/docker-joomla-part-2)), we've seen how to create a Joomla site from scratch by using Docker: pull Joomla, PHP, Apache and MySQL from Docker Hub and do magic stuff so we have a fresh `http://127.0.0.1:8080` local site. In this article, we'll see how to restore on our machine, a backup created thanks to great **[Akeeba Backup](https://www.akeeba.com/products/akeeba-backup.html)** component. We'll reuse some files from [Part 2](/blog/docker-joomla-part-2) and make some changes to them. At the end, we'll have a `make import` command that will start **[Akeeba Kickstart](https://www.akeeba.com/download.html#kickstart)**. ## Result Fill in the database info shown in your console, and Akeeba Kickstart takes care of the rest — from a raw `.jpa` backup to a browsable site: ![Akeeba Kickstart](./images/kickstart.webp) ![Restoring the database](./images/database.webp) ![Site is restored](./images/site_is_restored.webp) ## Why it works * We reuse the same `compose.yaml`/`.env`/`makefile` trio from [Part 2](/blog/docker-joomla-part-2), so nothing new to learn there. * The only real change: since the backup already **is** a full Joomla site, the containers only need PHP and Apache, not a fresh Joomla image — the database itself gets rebuilt from the backup by Akeeba Kickstart, not by Joomla's installer. ## Creating required files > **Skipping the files creation process:** > > In the next chapters, you'll need to create several files using copy/paste. If you wish to go faster, just run the following commands to retrieve files from my [Github repo](https://github.com/cavo789/docker_joomla_restore) so you don't need to create them. > > ```bash > $ mkdir /tmp/joomla_restore && cd $_ $ curl -LOJ --silent > https://github.com/cavo789/docker_joomla_restore/archive/refs/tags/1.0.0.tar.gz > $ tar -xzvf docker_joomla_restore-1.0.0.tar.gz --strip-components 1 && rm -f > docker_joomla_restore-1.0.0.tar.gz $ tar -xzvf docker_joomla_restore.gz > --strip-components 1 && rm -f docker_joomla_restore.gz > ``` > > By following the instructions below, you'll create a xxx folder where you can copy all the files you need to restore a Joomla site using Docker. You can then proceed immediately to the “Run the import process” chapter, as the files are already in place. *If you prefer to understand what you do and create files manually, please continue reading below.* Please create a new folder on your computer like f.i. `mkdir /tmp/joomla_restore && cd $_`. ```bash $ mkdir /tmp/joomla_restore && cd $_ ``` In that folder, you'll need to create several files (please refer to [Part 2](/blog/docker-joomla-part-2)) if you need more explanations about these files. ### The orchestration file: compose.yaml Below, highlighted, small differences from the `compose.yaml` file we saw in [Part 2](/blog/docker-joomla-part-2#updated-composeyaml). Here is why: this time we don't need to download a fresh copy of Joomla since **we already have a Joomla site** (our backup). What we need is **PHP** and **Apache**. We don't need Joomla, so this time we'll use a custom Docker image, not an already existing one. Except for the highlighted lines, everything else is the same as in [Part 2](/blog/docker-joomla-part-2). **compose.yaml** ```yaml name: ${PROJECT_NAME:-your-project-name} services: joomla: # highlight-next-line build: # highlight-next-line context: . # highlight-next-line args: # highlight-next-line - PHP_VERSION=${PHP_VERSION:-8.3-apache} container_name: ${CONTAINER_PREFIX:-joomla}-app restart: always ports: - ${WEB_PORT:-8080}:80 environment: - JOOMLA_DB_HOST=joomladb - JOOMLA_DB_NAME=${DB_NAME:-joomla} - JOOMLA_DB_PASSWORD=${DB_PASSWORD:-examplepass} - JOOMLA_DB_USER=${DB_USER:-joomla} depends_on: joomladb: condition: service_healthy user: ${UID:-1000}:${GID:-1000} volumes: - ./joomla_data:/var/www/html networks: - joomla_network joomladb: image: mysql:${MYSQL_VERSION:-latest} container_name: ${CONTAINER_PREFIX:-joomla}-db restart: always ports: - ${MYSQL_PORT-:3306}:3306 environment: - MYSQL_DATABASE=${DB_NAME:-joomla} - MYSQL_PASSWORD=${DB_PASSWORD:-examplepass} - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD:-rootpass} - MYSQL_USER=${DB_USER:-joomla} healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] timeout: 20s retries: 10 user: ${UID:-1000}:${GID:-1000} volumes: - ./db_data:/var/lib/mysql networks: - joomla_network phpmyadmin: image: phpmyadmin container_name: ${CONTAINER_PREFIX:-joomla}-phpmyadmin restart: always depends_on: joomladb: condition: service_healthy ports: - 8081:80 environment: - PMA_HOST=joomladb - PMA_PASSWORD=${DB_ROOT_PASSWORD:-rootpass} networks: - joomla_network networks: joomla_network: ``` ### The .env file As introduced above, we don't need Joomla anymore, just PHP and Apache. We'll then define a `PHP_VERSION` variable and initialize it to the desired version. **.env** ``` CONTAINER_PREFIX=joomla DB_NAME=joomla DB_PASSWORD=examplepass DB_USER=joomla MYSQL_PORT=3306 MYSQL_VERSION=8.4.2 // highlight-next-line PHP_VERSION=8.3-apache PROJECT_NAME=running-joomla-in-docker WEB_PORT=8080 ``` > **Caution:** > > Since we need to restore an existing site, make sure to use the same PHP version as the one used by your site. ### Our makefile We'll need a new action called `import`: **makefile** ``` -include .env config: @UID=$$(id -u) GID=$$(id -g) docker compose config down: stop -@UID=$$(id -u) GID=$$(id -g) docker compose down log: -@UID=$$(id -u) GID=$$(id -g) docker compose logs reset: down -@rm -rf db_data joomla_data start: @clear @printf "\033[1;33m%s\033[0m\n\n" "To start your site, please jump to http://127.0.0.1:${WEB_PORT}" @printf "\033[1;33m%s\033[0m\n\n" "Go to http://127.0.0.1:${WEB_PORT}/administrator to open your backend." @printf "\033[1;104m%s\033[0m\n\n" "Below a summary of your current installation:" @printf "\033[1;34m%s\033[0m\n\n" "JOOMLA" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Project name" "${PROJECT_NAME}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Version" "${JOOMLA_VERSION}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n\n" " * Port" "${WEB_PORT}" @printf "\033[1;34m%s\033[0m\n\n" "DATABASE" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Host" "joomla-db" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * User name" "${DB_USER}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Password" "${DB_PASSWORD}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Database name" "${DB_NAME}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Version" "${MYSQL_VERSION}" @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n\n" " * Port" "${MYSQL_PORT}" stop: -@UID=$$(id -u) GID=$$(id -g) docker compose stop up: -@mkdir -p db_data joomla_data @UID=$$(id -u) GID=$$(id -g) docker compose up --detach --build # highlight-next-line import: reset # highlight-next-line -@mkdir -p db_data joomla_data # highlight-next-line # highlight-next-line @clear # highlight-next-line @printf "\033[1;33m%s\033[0m\n" "Copying Akeeba Kickstart to the Docker Joomla container..." # highlight-next-line @cp en-GB.kickstart.ini ./joomla_data # highlight-next-line @cp kickstart.php ./joomla_data/kickstart.php # highlight-next-line # highlight-next-line @printf "\033[1;33m%s\033[0m\n\n" "Copying your backup to the Docker Joomla container..." # highlight-next-line @cp backup.jpa ./joomla_data # highlight-next-line # highlight-next-line @UID=$$(id -u) GID=$$(id -g) docker compose up --detach # highlight-next-line # highlight-next-line @printf "\n\n\033[1;33m%s\033[0m\n\n" "Please now jump to http://127.0.0.1:${WEB_PORT}/kickstart.php to finalize your site restoration." # highlight-next-line # highlight-next-line @printf "\033[1;104m%s\033[0m\n\n" "Below a summary of your current installation:" # highlight-next-line # highlight-next-line @printf "\033[1;34m%s\033[0m\n\n" "JOOMLA" # highlight-next-line @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n\n" " * Port" "${WEB_PORT}" # highlight-next-line # highlight-next-line @printf "\033[1;34m%s\033[0m\n\n" "DATABASE" # highlight-next-line @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Host" "joomla-db" # highlight-next-line @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * User name" "${DB_USER}" # highlight-next-line @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Password" "${DB_PASSWORD}" # highlight-next-line @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Database name" "${DB_NAME}" # highlight-next-line @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n" " * Version" "${MYSQL_VERSION}" # highlight-next-line @printf "\033[1;34m%-30s\033[0m\033[1;104m%s\033[0m\n\n" " * Port" "${MYSQL_PORT}" ``` > **Caution:** > > If you don't know if you already have `GNU make`, just run `which make` in the console. If you see `make not found` then please run `sudo apt-get update && sudo apt-get install make` to proceed with the installation. > > ```bash > $ which make > ``` > > ```bash > $ sudo apt-get update && sudo apt-get install make > ``` ### We also need a file called Dockerfile In the very first lines of the `compose.yaml` file we've this portion: **compose.yaml** ```yaml name: ${PROJECT_NAME:-your-project-name} services: joomla: # highlight-next-line build: # highlight-next-line context: . # highlight-next-line args: # highlight-next-line - PHP_VERSION=${PHP_VERSION:-8.3-apache} ``` We don't see here an `image:` statement but a `build:` one. This means that we need a special file called `Dockerfile`. Please create a new file called `Dockerfile` in your folder with this content: **Dockerfile** ```docker ARG PHP_VERSION=8.3-apache FROM php:${PHP_VERSION:-latest} RUN set -e -x ; \ docker-php-ext-install mysqli pdo pdo_mysql && docker-php-ext-enable pdo_mysql ``` That file is quite simple right now: it tells us we need a PHP Docker image, and we also need to install the `mysqli` drivers. ### We need a backup for sure Please also put a backup of your existing site and save it in the same folder. For standardization needs, please call that file `backup.jpa`. To make this step more explicit: jump to your existing Joomla website and, using Akeeba Backup, create a backup (file format: `.jpa`) and download the backup. Please rename the backup file like this: `backup.jpa`. ### And finally, we need Akeeba Kickstart Finally, we also need to have two files coming from **[Akeeba Kickstart](https://www.akeeba.com/download.html#kickstart)**. By following the link [Akeeba Kickstart](https://www.akeeba.com/download.html#kickstart), we'll be able to download a `.zip` file (called `kickstart-core-xxx.zip` where `xxx` is a version number). Proceed to the download and open the archives: we'll get two files in it. Please copy these files in your folder. The two files we need are called `en-GB.kickstart.ini` and `kickstart.php`. ## Before starting We're ready. Just before you begin, please check that you have all the required files (please run `ls -alh`): ```bash $ ls -alh Permissions Size User Group Date Modified Name drwxr-xr-x - christophe christophe 2024-10-18 13:30 . drwxrwxrwt - root root 2024-10-18 13:30 .. .rw-r--r-- 151 christophe christophe 2024-10-18 13:30 .env .rw-r--r-- 34M christophe christophe 2024-10-18 13:30 backup.jpa .rw-r--r-- 1.3k christophe christophe 2024-10-18 13:30 compose.yaml .rw-r--r-- 162 christophe christophe 2024-10-18 13:30 Dockerfile .rw-r--r-- 11k christophe christophe 2024-10-18 13:30 en-GB.kickstart.ini .rw-r--r-- 390k christophe christophe 2024-10-18 13:30 kickstart.php .rw-r--r-- 2.9k christophe christophe 2024-10-18 13:30 makefile ``` > **Skipping the files creation process:** > > If you've skipped the file creation step earlier by downloading files from my repo, you'll have all the files listed above. > **Caution:** > > Remember, for Linux users, we need `GNU make` to be able to run commands like `make import` or `make up`. If you see `make not found` then please run `sudo apt-get update && sudo apt-get install make` to proceed with the installation. ## Run the importation process Ok, you've copied a lot of files. Time to start restoring your site. For Linux users, simply take advantage of `GNU Make` and run the `make import` instruction. For Windows (Powershell or DOS), please run these commands: ```bash mkdir db_data mkdir joomla_data copy en-GB.kickstart.ini joomla_data\ copy kickstart.php joomla_data\ copy backup.jpa joomla_data\ docker compose up --detach ``` For both users (Linux and Windows), you'll get something like this output in the console: ```bash [+] Building 1.4s (7/7) FINISHED docker:default => [joomla internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 200B 0.0s => [joomla internal] load metadata for docker.io/library/php:8.3-apache 1.2s => [joomla internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s [...] => [joomla] resolving provenance for metadata file 0.0s [+] Running 3/3 ✔ Network running-joomla-in-docker_joomla_network Created 0.0s ✔ Container joomla-db Healthy 31.0s ✔ Container joomla-app Started 31.3s ``` When finished, just start your browser and surf to `http://127.0.0.1:8080/kickstart.php` to start the restoration of your website using the **Akeeba Kickstart** wizard — the screens shown at the top of this article. Once you need to fill in the database information, please refer to what was displayed in your console. Continue the restoration as you always have; close the window once Akeeba Kickstart asks for it, then click on the *Clean Up* button; then on *Visit your site's frontend* and enjoy. > **Information:** > > If you've used the provided demo site, the admin account is **joomla** and its password is **joomla\@secured**. ## Conclusion As we've just seen, restoring a Joomla site locally has become really simple. All you need to do is retrieve a few configuration files, and just the one named `.env` should be updated with the PHP and MySQL versions you're using on your production site. You download the local backup of your site and, with a single command (`make import`), your site is restored. ```bash $ make import ``` No matter what version of Joomla or MySQL you're using. Piece of cake, no? ---