I am a big Nextcloud user. I use it for my files, notes, images, videos, calendar, mail, newsfeed, collaboration, and much more. So when I couldn’t find an application that seemed a natural match for Nextcloud, I decided, I could try to build it myself. The last time I worked on a Nextcloud app, I used a complicated Vagrant setup, that worked but didn’t feel right. That’s why I tried see if I could create an easier version with docker and docker-compose.
These where my requirements:
- IDE support for code completion. I use PHPStorm and I want to see no squiggly lines because some base class could not be found.
- Same with Xdebug support. I need my breakpoints!
- PHPUnit, yes thank you very much
- No git clone or setting up anything for Nextcloud. Just my app code and nothing else should have to be touched by me.
After some trial and error, I think I managed to find a way to meet all these requirements with little hacks needed.
The Recipe
Create a new empty directory for your app, with another empty
src
directory inside that. Change the permissions for the src folder to 777:
chmod 777 src
The Dockerfile
Create a new Dockfile and paste this content:
FROM nextcloud:23-apache
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug \
&& echo "xdebug.client_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
&& echo "xdebug.idekey=PHPSTORM" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
&& echo "xdebug.start_with_request=trigger" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
&& echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php -r "if (hash_file('sha384', 'composer-setup.php') === '906a84df04cea2aa72f40b5f787e49f22d4c2f19492ac310e8cba5b96ac8b64115ac402c8cd292b8a03482574915d1a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
&& php composer-setup.php \
&& php -r "unlink('composer-setup.php');" \
&& mv composer.phar /usr/local/bin/composer
Line 1 pulls the latest Nextcloud Docker image (at the time of writing) with a built-in Apache server. This contains everything we need to run Nextcloud locally.
Line 3-8 installs and enables Xdebug and also writes the Xdebug config so that we can do some step debugging. You can find the documentation for the config
here. These are the four entries that you need to have:
client_host=host.docker.internal
will resolve to the IP address of your docker host, so that your IDE can receive the Xdebug events. For this to work, you need to make an entry in the docker-compose file (at least on Linux). I will get into that later.idekey=PHPSTORM
I’m using PHPStorm, so this is what I had to put there.start_with_request=trigger
this will cause Xdebug to send events only if a certain request parameter is set. This is done by an extension that I use for Firefox but I’m sure there are plenty of options for Chrome based browsers out there. Basically, this setting allows me to enable Xdebug when I need it and disable it for more speed when I don’t. If you want Xdebug enabled for every request, just enter yes
instead of trigger
.xdebug.mode=debug
this setting enables step debugging.
Line 11-15 installs composer, if we may need to setup PHPUnit or add more dependencies to our App.
The docker-compose file
Copy this contents to a new docker-compose.yml
services:
app:
build: .
ports:
- 8080:80
environment:
SQLITE_DATABASE: nextcloud.db
NEXTCLOUD_ADMIN_USER: nextcloud
NEXTCLOUD_ADMIN_PASSWORD: nextcloud
volumes:
- ./src:/var/www/html
extra_hosts:
- "host.docker.internal:host-gateway"
Line 3 builds the Dockerfile that’s within the same directory.
Lines 4-5 map the port 8080 of the docker host to port 80 of the container. We can now access our Nextcloud through
http://localhost:8080
Line 7 names the SQLite database and lines 8-9 create a user with password, so we don’t have to go through the setup wizard.
Line 11 mounts our src folder as the root folder of Nextcloud. That means, that
a lot of files will be popping up in the src folder when the container is started. We need them for code completion and for our IDE not being completely lost.
Line 13 is necessary on Linux AFAIK to make this nice
xdebug.client_host=host.docker.internal
hack possible.
Now, we can run our Nextcloud with
docker-compose up
You should be able to visit
http://localhost:8080
and login.
Creating the app
Create an app skeleton in the
app store.
Download the package and unpack it somewhere.
Since the files in the src folder where created by the user running inside the docker container, they are technically not ours. But we need to be able to write to the custom_apps directory to create our app. Therefore, we need to change the ownership of that folder (
and that folder only!):
sudo chown username:username src/custom_apps/appname -R
Now, copy the unpacked app files into the custom_apps directory:
cp -r skeleton_app src/custom_apps
/
Enabling the app
Last thing to do is to enable the app in our Nextcloud. Go to
Apps and search for the name of your app and enable it. You will need to enable custom apps during that step also.
That’s it. You should now have a dockerized Nextcloud with Xdebug and your app running. Last thing I would do is
git init
in your app folder and push it somewhere because if for some reason your deleting your docker container and run it again from scratch, it will
delete everything in the source folder. I learned the hard way.
Thanks for this! A couple points of clarification:
> Create a new empty directory for your app, with another empty src directory inside that. Change the permissions for the src folder to 777: chmod 777 src
This directory isn’t for your app but is for the Dockerfile and docker-compose.yml. I believe if you create this directory as yourself (not as root or www-user or whatever) then you won’t need to change the mode from 755.
> But we need to be able to write to the custom_apps directory to create our app. Therefore, we need to change the ownership of that folder (and that folder only!): sudo chown username:username src/custom_apps/appname -R Now, copy the unpacked app files into the custom_apps directory: cp -r skeleton_app src/custom_apps/
Since I created the directory in step 1 as myself, I didn’t need to chown anything. Anyways, you probably won’t be able to unless you copy the appname directory first, or create an empty one in its place and copy just the contents.
Have you ever tried using podman? I’m curious if the steps would be very different, other than the lack of a compose file obviously.
Thanks for your comments, Dave!
As far as I know, the container will mount
src
and write into it aswww-data
user, but the folder belongs to root (or at least an unknown user) inside the container. I had permission issues with nextcloud being unable to install until I changed the permissions.When I created the folder first, the whole custom_apps folder was deleted during startup. That was a funny lesson to learn.
I tried using podman, but it didn’t immediately work and then podman took over the docker cli and I was simply to lazy to figure out, how to do it right. I still have my occasional fights with docker and in my limited free time, I don’t want to scream at new technology.
Curious why permissions would be different, perhaps because I’m using a Mac…
As for limited free time, I totally understand. I’ve been wanting to contribute to Nextcloud for years and just now making a concentrated effort.
In particular, I want to do something with the text app. However, it requires Nextcloud 24, which doesn’t have an official Docker image yet. So I copied the 23-apache Dockerfile and replaced the final RUN command — which downloads and extracts the latest release tarball — with instructions to clone the server, text and viewer repos. That didn’t work so well, so I cloned the repositories outside of the Dockerfile and added COPY commands. It takes a very long time, but it works: https://gist.github.com/dave-kennedy/38e45f16f0d47e4b828891fe814e0d7c
Any ideas how that could be improved?
I mean, a Mac is using a VM to run Linux to run docker… I have absolutely no idea. Maybe magic?
Also, I am surprised that you are extending an apache docker image and not the PHP image. That might make things easier and faster to build. Other than that, if it works, it works!