[HOWTO] Running Nextcloud over self-signed HTTPS (SSL/TLS) in docker

So you don’t want to run a reverse proxy but you need to serve your Nextcloud over HTTPS? Here’s a quick guide to enable SSL on Apache within the container.

Creating the certificate

Creating the certificate is out of the scope of this howto, but you don’t have to look far to find information on how to generate one.

However you do it, copy the certificate and key files to a directory of your choice (I will use /etc/ssl/mydomain/). Use the filenames cert.pem and key.pem.

With an authority

There are a million guides on the internet, as well as official documentation, on how to generate a certificate with letsencrypt. Follow one of these to set up Nextcloud with a public domain name or subdomain you control.

Self-signing a certificate

If you’re running Nextcloud locally, or on a VPN with an internal IP and domain, you can’t use letsencrypt to generate your certiciates, so you will have to self-sign one.

I recommend also creating a certificate authority and signing the certificate. Then you can import the CA’s cert into your browser to prevent errors. Here’s a tutorial on how to do this, but you can google for about 1,000 more.

Installing SSL on the image

Create a Dockerfile in the directory you will run nextcloud from. I will use /srv/nextcloud/ in this example. Change admin@domain.tld and nextcloud.domain.tld to and admin email and your site’s domain, respectively.

$ cat Dockerfile
FROM nextcloud:apache
COPY setssl.sh /usr/local/bin/
RUN /usr/local/bin/setssl.sh admin@domain.tld nextcloud.domain.tld

This dockerfile inherits from nextcloud and runs our setssl.sh script while ithe container boots. This is the recommended method outlined in the docker hub readme under Adding Features.

This is the script we’ll run in the container as it’s starting. Save it to setssl.sh and give it executable permissions.

# setssl.sh
# USAGE: setssl.sh <email> <domain>

SSLProtocol All -SSLv2 -SSLv3
SSLHonorCipherOrder On
Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains"
Header always set X-Frame-Options DENY
Header always set X-Content-Type-Options nosniff
SSLCompression off
SSLSessionTickets Off' > /etc/apache2/conf-available/ssl-params.conf
echo "<IfModule mod_ssl.c>
        <VirtualHost _default_:443>
                ServerAdmin $2
                ServerName $1
" > /etc/apache2/sites-available/default-ssl.conf
echo '
                DocumentRoot /var/www/html

                ErrorLog ${APACHE_LOG_DIR}/error.log
                CustomLog ${APACHE_LOG_DIR}/access.log combined

                SSLEngine on

                SSLCertificateFile    /etc/ssl/nextcloud/cert.pem
                SSLCertificateKeyFile /etc/ssl/nextcloud/key.pem

                <FilesMatch "\.(cgi|shtml|phtml|php)$">
                                SSLOptions +StdEnvVars
                <Directory /usr/lib/cgi-bin>
                                SSLOptions +StdEnvVars
</IfModule>' >> /etc/apache2/sites-available/default-ssl.conf
a2enmod ssl >/dev/null
a2ensite default-ssl >/dev/null
a2enconf ssl-params >/dev/null

Finally, modify your docker run or docker-compose command to include the changes.


Don’t forget to replace /etc/ssl/mydomain with the path to your actual directory.


Change the nextcloud service lines in your docker-compose.yml. You can use the one from Docker Hub as a basis.

#  image: nextcloud:latest
  build: .
  container_name: nextcloud
  restart: unless-stopped
#    - "8080:80"
    - "8443:443"
    - "./nextcloud:/var/www/html"
    - "/etc/ssl/mydomain:/etc/ssl/nextcloud"

docker run

First, build the new image described in the dockerfile. Then you can run it. You’ll have to mount the directory with the ssl certs as a volume, and use port 443 instead of 80. Something like this:

$ docker build --tag nextcloud_ssl .
$ docker run -d \
    -p 8443:443
    -v nextcloud:/var/www/html \
    -v /etc/ssl/mydomain:/etc/ssl/nextcloud


Like it says in the readme:

Updating your own derived image is also very simple. When a new version of the Nextcloud image is available run:

docker build -t your-name --pull .
docker run -d your-name

or for docker-compose:

docker-compose build --pull
docker-compose up -d

The --pull option tells docker to look for new versions of the base image. Then the build instructions inside your Dockerfile are run on top of the new image.