diff --git a/.gitignore b/.gitignore
index 20ebc05bd..de468221c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
local.yml
+assets/certs/certs
+assets/certs/ca
diff --git a/README.md b/README.md
index e8eafadef..e8b57eaf4 100644
--- a/README.md
+++ b/README.md
@@ -220,6 +220,41 @@ When you change them, use `bin/moodle-docker-compose down && bin/moodle-docker-c
| `MOODLE_DOCKER_APP_RUNTIME` | no | 'ionic3' or 'ionic5' | not set | Set this to indicate the runtime being used in the Moodle app. In most cases, this can be ignored because the runtime is guessed automatically (except on Windows using the `.cmd` binary). In case you need to set it manually and you're not sure which one it is, versions 3.9.5 and later should be using Ionic 5. |
| `MOODLE_DOCKER_APP_NODE_VERSION` | no | [node](https://hub.docker.com/_/node) image version tag | not set | Node version to run the app. In most cases, this can be ignored because the version is parsed from the project's `.nvmrc` file. This will only be used when the runtime is `ionic5` and the app is running from the local filesystem. |
+## SSL certificates
+
+If you wish, you can generate a self-signed certificate repository and create your own certificates for your containers.
+
+Certificates should be placed into the `assets/certs/certs` directory, with the certificate authority placed into `assets/certs/ca`.
+
+If moodle-docker-compose detects the certificate for a local certificate authority at `asserts/certs/ca/ca.pem` then an additional SSL configuration will be included.
+
+To make this easier, a helper has been created for Linux and MacOS which will:
+
+- generate a new certificate authority if required
+- generate certificates
+- offer to install your new Certificate Authority
+
+
+The helper can be used as follows:
+
+```
+./assets/certs/createcerts.sh hostname [alternative-hostname]
+```
+
+You will need to run a separate command for each host you wish to generate a certificate for, for example:
+
+```
+./asserts/certs/createcerts.sh webserver webserver.container.docker.internal
+./asserts/certs/createcerts.sh exttests exttests.container.docker.internal
+```
+
+### Local hostnames
+
+If you wish to access your containers using their certificates, you will need to add either:
+
+- entries to your `/etc/hosts` or `%WinDir%\System32\Drivers\Etc\Hosts` file; or
+- relevant DNS entries to your local DNS resolver (for example dnsmasqd).
+
## Local customisations
In some situations you may wish to add local customisations, such as including additional containers, or changing existing containers.
diff --git a/assets/certs/443-default.conf b/assets/certs/443-default.conf
new file mode 100644
index 000000000..f54cff145
--- /dev/null
+++ b/assets/certs/443-default.conf
@@ -0,0 +1,9 @@
+
+ ServerName webserver
+ DocumentRoot /var/www/html
+ ErrorLog /var/log/apache2/error.log
+ CustomLog /var/log/apache2/access.log combined
+ SSLEngine on
+ SSLCertificateFile "/etc/ssl/certs/moodle/webserver.crt"
+ SSLCertificateKeyFile "/etc/ssl/certs/moodle/webserver.key"
+
diff --git a/assets/certs/README.md b/assets/certs/README.md
new file mode 100644
index 000000000..252585fd0
--- /dev/null
+++ b/assets/certs/README.md
@@ -0,0 +1,19 @@
+# IMPORTANT
+
+These are test Certificates and Keys only!
+
+Do NOT use them outside of a closed development environment.
+
+DO NOT commit them to the git repository.
+
+## Generating new certificates
+
+A helper script exists at the project root and takes a list of names for the certificate.
+
+For example:
+
+```
+./createcerts.sh webserver webserver.container.docker.internal
+```
+
+Each argument is used as a subject alternative name for the certificate.
diff --git a/assets/certs/createcerts.sh b/assets/certs/createcerts.sh
new file mode 100755
index 000000000..4516a7847
--- /dev/null
+++ b/assets/certs/createcerts.sh
@@ -0,0 +1,155 @@
+#!/bin/bash
+
+CERTDIR="`pwd`/assets/certs/"
+
+CACONF="${CERTDIR}/openssl.cnf"
+CAKEY="${CERTDIR}/ca/ca.key"
+CACERT="${CERTDIR}/ca/ca.pem"
+
+if [ -f "$CAKEY" ] && [ -f "${CACERT}" ]; then
+ echo "Using existing CA private key"
+ echo
+else
+ # Generate the private key for the CA:
+ echo "Generating the key and certificate for the CA server"
+ mkdir -p "${CERTDIR}/ca"
+ mkdir -p "${CERTDIR}/certs"
+
+ # Generate the key and certificate for the CA.
+ cat < "${CERTDIR}/ca/serial.txt"
+ echo
+ echo "You should add this certificate to your root certificate store."
+
+ OS=`uname -s`
+ if [ "${OS}" = "Darwin" ]
+ then
+ echo "You can use the following command:"
+ echo "sudo security add-trusted-cert -d -r trustRoot -k '/Library/Keychains/System.keychain' ${CACERT}"
+ read -p "Do you want me to do that for you now? " yn
+ case $yn in
+ [Yy]* ) sudo security add-trusted-cert -d -r trustRoot -k '/Library/Keychains/System.keychain' "${CACERT}"; break;;
+ esac
+ fi
+
+ if [ "${OS}" = "Linux" ]
+ then
+ echo "You can use the following command:"
+ echo "sudo cp ${CERTDIR}/ca/ca.pem usr/local/share/ca-certificates/moodle-docker-ca.crt && sudo update-ca-certificates"
+ read -p "Do you want me to do that for you now? " yn
+ case $yn in
+ [Yy]* ) sudo cp "${CERTDIR}/ca/ca.pem" usr/local/share/ca-certificates/moodle-docker-ca.crt && sudo update-ca-certificates; break;;
+ esac
+
+ fi
+fi
+
+if [ "$#" -lt 1 ]
+then
+ echo "Usage: Must supply at least one hostname."
+ exit 1
+fi
+
+# The first hostname is canonical.
+DOMAIN=$1
+
+HOSTKEY="${CERTDIR}/certs/${DOMAIN}.key"
+HOSTCSR="${CERTDIR}/certs/${DOMAIN}.csr"
+HOSTCRT="${CERTDIR}/certs/${DOMAIN}.crt"
+HOSTEXT="${CERTDIR}/certs/${DOMAIN}.ext"
+
+# Create a private key for the dev site:
+echo
+echo "Generating a private key for the $DOMAIN dev site"
+echo
+openssl genrsa -out "${HOSTKEY}" 2048
+
+echo "Generating a CSR for $DOMAIN"
+cat < "${HOSTEXT}" << EOF
+[ req ]
+default_bits = 2048
+default_keyfile = ${HOSTKEY}
+distinguished_name = server_distinguished_name
+req_extensions = server_req_extensions
+string_mask = utf8only
+
+[ server_distinguished_name ]
+
+countryName = Country Name (2 letter code)
+countryName_default = AU
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = Western Australia
+
+localityName = Locality Name (eg, city)
+localityName_default = Perth
+
+organizationName = Organization Name (eg, company)
+organizationName_default = Moodle Pty Ltd
+
+organizationalUnitName = Organizational Unit (eg, division)
+organizationalUnitName_default = Moodle LMS
+
+commonName = Common Name (e.g. server FQDN or YOUR name)
+commonName_default = ${DOMAIN}
+
+emailAddress = Email Address
+emailAddress_default = moodle@example.com
+
+[ server_req_extensions ]
+subjectKeyIdentifier = hash
+basicConstraints = CA:FALSE
+keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
+subjectAltName = @alternate_names
+[ alternate_names ]
+$DNS
+EOF
+
+#Next run the command to create the certificate: using our CSR, the CA private key, the CA certificate, and the config file:
+echo "Generating a certificate for $DOMAIN"
+cat <