[OUTDATED] Nextcloud + nginx reverse proxy + letsencrypt in docker containers

The guide is outdated! Please visit the [official github] (https://github.com/nextcloud/docker) for up-to-date infos and help.


I’m playing around with docker containers and created a docker-compose file for a full nextcloud installation including

  • Nginx reverse proxy
  • Automated creation and renew of letsencrypt ssl certificates
  • Nextcloud container,
  • mariaDB

I used the following images:

I thought someone could use it so I share it here.

Since the nextcloud docker container is not official and has some bugs / incompatibilities, I would not recommend using it in a production environment or even at home at this point!

There are some people working on an official nextcloud container, see here and on GitHub, but nothing is ready yet.


version: ‘2’

image: nginx
container_name: nginx
- 80:80
- 443:443
- ./proxy/conf.d:/etc/nginx/conf.d
- ./proxy/vhost.d:/etc/nginx/vhost.d
- ./proxy/html:/usr/share/nginx/html
- ./proxy/certs:/etc/nginx/certs:ro
- proxy-tier

image: jwilder/docker-gen
container_name: nginx-gen
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./proxy/templates/nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro
- nginx
entrypoint: /usr/local/bin/docker-gen -notify-sighup nginx -watch -only-exposed -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf

image: jrcs/letsencrypt-nginx-proxy-companion
container_name: letsencrypt-companion
depends_on: [nginx-gen]
- nginx
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./proxy/certs:/etc/nginx/certs:rw

image: wonderfall/nextcloud
container_name: nextcloud
- db_nextcloud:db_nextcloud
- UID=1000
- GID=1000
- ./nextcloud/data:/data
- ./nextcloud/config:/config
- ./nextcloud/apps:/apps2
- VIRTUAL_HOST=domain_name
- VIRTUAL_NETWORK=nginx-proxy
- LETSENCRYPT_HOST=domain_name
- LETSENCRYPT_EMAIL=certificate_mail_address
- proxy-tier
- letsencrypt-nginx-proxy-companion

db_nextcloud: #database
image: mariadb:10
container_name: db_nextcloud
- ./nextcloud/db:/var/lib/mysql
- MYSQL_ROOT_PASSWORD=root_password
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=database_password
- proxy-tier
- letsencrypt-nginx-proxy-companion

name: nginx-proxy

Before runnig docker-compose there are three things that need to be done.

  1. Create a virtual network called nginx-proxy (source)

docker network create -d bridge nginx-proxy

  1. Download the nginx proxy template file

mkdir ./proxy/templates -p
curl https://raw.githubusercontent.com/jwilder/nginx-proxy/master/nginx.tmpl > ./proxy/templates/nginx.tmpl

  1. Replace the bold strings in the docker-compose file. (You might want to replace the path volume directories on the host from ./___ to absolute paths. Consider this in step 2)

To run the hole configuration simply type

docker-compose up -d

The letsencrypt container may take a few moments to create the certificates but all this is handled automatically.
You can now access your nextcloud installation from your domain and the configuration page shows up. Use the password you chose in the docker-compose file and for server the database container name (db_nextcloud in this case). Additional configuration is described here.

For now there might be a little bug that let’s the configuration page show up again everytime the container is restartet. Simply adding the following lines manually into the nextcloud config.php solves this issue.

‘installed’ => ‘true’,

Updates for any container are als simple as using docker-compose pull and docker-compose up -d again.

Have Fun with your containers ;),

1 Like

If I give you the ability to commit to our github, will you help out the others working on the docker image? :wink:

When trying create this (doing a copy paste of the docker-compose file I keep running into the following error

$ docker-compose up ERROR: In file './docker-compose.yml', service must be a mapping, not a NoneType.

Hey @Undeadhunter,

it’s an indentation problem. In YAML indentations are used to structure the data.

For now use:

Does anyone know how to fix this in my post? I can’t get the indentations to work properly. (In fact i can’t get any indentation in the blockquote.)

@jospoortvliet I don’t think I got enough experience to work directly on the project, but I appreciate the offer.
As I said, I’m just playing around and working myself through tutorials. In fact I doesn’t even get half of what’s happening behind the scenes. :wink:

If I see an oppertunity to contribute I’ll bring in a PR.

Cool, that works :wink:

I gave some love to the official docker container:

Would be glad if you can test it, and give some feedback.

For the compose part, I linked to my repo for now, but we could replace it with your amazing setup (I recommend the same actually).

I still have trouble to know what is the best default?

Is the best default providing an image that exposes the port 80, and then tell the user to install a proper TLS reverse proxy?
Or is the best default exposing 443 secured by letsencrypt and let the tech-savvy out there do the modifications they need to fit their docker infrastructure?

I’ll give it a try the next days.

For the default configuration I struggle to find a good solution too. The main goal should be an easy to setup plug-and-play solution, that needs as few settings as possible. For my part I prefer a more complete solution, entering my admin/database user and password and I’m good to go. So I like AiO-solutions and/or docker as it feels much more clean as installing everything by hand on the server. For me it would be fine to run just one docker container.

When I created this compose-file I could’t find a complete solution including encryption, so I started tinkering around and found this neat solution :wink:

This has a downside and an upside. It relies on several 3rd party containers. Even though I used official containers where possible and otherwise the most often used ones, there might be problems there. On the other hand we don’t have to maintain things like configuring renewal of SSL-certificates as the 3rd-party container does it.

For now I tend to like the split of concerns as I worked with it.

  • Nextcloud instance
  • Database
  • LetsEncrypt certificate container with automatic renewal
  • Nginx reverse proxy
  • automatic reverse proxy configuration container

But I can’t say if this is the way to go.

Sorry, if this wasn’t helpfull, but this are my thoughts on that topic. :worried:

Hi there

Sorry if this is a stupid question (still learning Docker)…but can you elaborate on your comment in step 3 above please [… (You might want to replace the path volume directories on the host from ./__ to absolute paths. Consider this in step 2_)…] ?

If I try and run the command in step 2 I get an error (No such file or directory).

If I am supposed to create a location (absolute path) then where should this be?

Also, if I am suppose to substitute absolute paths for the “path volume directories” as suggested, where should these point to?

Again, apologies if I am missing something very obvious but I would be most appreciative of any assistance to get this working for me!

Kind regards


PS - also keep getting errors with the docker-compose file:

ERROR: yaml.scanner.ScannerError: mapping values are not allowed here
   in "./docker-compose.yml", line 69, column 19

Tried to access the pastebin version (clicking the ‘Download as text’ link) but this also failed (after being made to create an UbuntuOne account)… it takes me to a page that says: 'Plain form not available for deep linking" ??

Any help with this would also be much appreciated!

Cheers :slight_smile:


The guide uses subfolders of the directory where your docker-compose.yml file is stored to save the nextcloud data, nginx settings and certificates.
The structure should look like this:



To run this setup you must download the template file first and store it in the right folder ./nginx/templates/nginx.tmpl. If it doesn’t exist yet you have to create it by hand. I’ll add that in the description.

On my machine I stored everything in a seperate folder that i created before (for example "/storage/proxy/… ", “/storage/nextcloud/…”, and so on). I thought it to be more clean than storing the data in a subfolder of the compose file.
You can basically use any location you want, just make sure the directory names in the compose file are correct.

For your errors:

  • When you download the template file, you might have to point to an existing folder. (not sure if curl can create parent folders). To make it easier just download to your current folder with

curl https://raw.githubusercontent.com/jwilder/nginx-proxy/master/nginx.tmpl36 > nginx.tmpl

and mkdir and cp after that.

  • Just copy the text from the pastebin link to an empty file and name it docker-compose.yml. You don’t need an account.

If you have any questions don’t hesitate to ask me again :slight_smile:

Hey mate… many thanks for the reply! I really appreciate it!!

I actually stumbled my way through it earlier today but thank you very much anyway for the clarification.

My next problem is that I seem to have everything running however I cannot access Nextcloud from the outside world? (Only locally on the server on which it is running).

It wasn’t clear to me if I was/am meant to modify the nginx.tmpl file at all (eg. tell it what my VIRTUAL_HOST / domains should be etc)?

I have checked and triple checked that I substituted the correct VIRTUAL_HOST & LETSENCRYPT_HOST details into the docker-compose.yml that you provided.

Is there somewhere else that I should put these details as well?

When I check the status of the containers I notice that they are all running (up) with the exception of the nginx-gen container which says it has ‘Exited’. Not sure if this is expected behaviour or…?

The only other thing I could think of was the perhaps it was a firewall issue however I have just checked and that seems okay (at least to my novice eyes). I am using UFW btw and below is the output of sudo ufw status verbose:

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
30088/tcp (OpenSSH)        ALLOW IN    Anywhere
22                         DENY IN     Anywhere
443/tcp                    ALLOW IN    Anywhere
80/tcp                     ALLOW IN    Anywhere

My understanding is that Docker can do some things behind the scenes to ip-tables etc but no doubt you are more than aware of this.

FYI I have this all set up on a VPS that is running Ubuntu 14.04 into which I am SSH’ing to set everything up.

Would be eternally grateful if you could help me get this working…feel like I am so close now I can taste it :slight_smile:


The nginx.tmpl file is not to be modified. To clarify what the containers are for:

  • the docker-gen(nginx-gen) container is connected to the docker api and watches changes of running containers (for example started and stopped containers). It then looks for evironment variables (like VIRTUAL_HOST, VIRTUAL_NETWORK, VIRTUAL_PORT) on this containers and uses them to create a nginx configuration that proxies requests to that specific container. When it makes changes it notifies the nginx container to reload / restart and use the updated settings.

  • nginx container is the reverse poxy that is accessible from the outside world and listens to all request (via http and https). It uses the configurations create by docker-gen the route request via the internal docker network to the containers.

  • letsencrypt-nginx-proxy-companion uses the docker-gen container to creates certificates with the letsencrypt-authority and enables https encryption for your domain names. It also automatically renwes certificates when there about to expire

(don’t ask me how this in particular works, i’m just using it ;))

  • the wonderfall/nextcloud container is a full nextcloud installation with an nginx webserver on it’s own. It exposes port 80 to the docker network (it’s not accessible from outside). Since it is only communicating in the docker network within the same machine here is no encryption needed. All https encryption is handled by the nginx proxy.

  • the db container is self-explenatory

Unfortunately there are some restriction when using the nginx reverse proxy container in this setup. Right now it’s just possible to create automatic nginx-configurations for subdomains of a domain (e.g. cloud.example.com from example.com). So you must have a domain name that can have subdomains and point them at the correct ip / cname.

I don’t think there is a way using this setup just locally with no connection to the internet as you need a connection to create and validate the certificates.

Thanks for the detailed reply!

I (think) I understand all that you have explained. However my issue is that I have been unable to get access from the outside. (I don’t want local access, I want access from anywhere).

So, just ticking off the prerequisites:

  1. I have a domain name setup with a subdomain (A records pointing at my VPS public IP address). Have confirmed this works with a single nginx docker container).

  2. according to your answer above - the only place I need to input the server (virtual host) address is within the docker-compose.yml file and I have confirmed that this has indeed been done correctly.

So given the above (and assuming I haven’t missed anything)…do you have any suggestions how I might troubleshoot this setup to figure out why I can’t get access from the outside?

Cheers again for all your help with this!


pls have a look and / or post the logs

docker-compose logs

Here ya go:

1 Like


I went through the guide again and could reproduce your error. Since I wrote the guide there where some changes made to the nginx.tmpl file and now the letsencrypt-companion fails making and validating certificates :frowning: .

Seems there are some fixes to this in open PRs, but i haven’t found a solution yet. If your interested have a look at:

I’ll investigate that matter the next days.

Hey thanks very much for the efforts. I’ll take a look at those and report back if/when I have any breakthroughs!

In the meantime, here is the latest docker-compose.yml (substituted my domain for a filler):


And the corresponding output from docker-compose logs


Thanks again for all your help with this my friend. Really looking forward to a solution asap (have a presentation at work that kind of relies on this :sweat: )

BTW - is there any chance we could notify the authors perhaps? (not sure the most efficient way to do that)

Okay - very quick update…

Have manually replaced the contents of nginx.tmpl with that of
spacediver’s commit:


(Note that I renamed his file: nginx.tmpl)

This has allowed me to access the Nextcloud container from the outside world! :grinning:
But… only non-encrypted (ie. http://hostdomain and not https://hostdomain) :confused:

So at least this is some progress. Now if we could just work out how I can get the https version working I will be a very happy camper!

Have posted here also:


Didn’t make much progress with this today :frowning: