In this blog post, I’ll walk you through installing Red Hat Quay using Podman containers, setting up the necessary PostgreSQL and Redis dependencies, and then securing your registry with a reverse proxy (nginx) and Let’s Encrypt SSL certificates behind Cloudflare. This approach provides a production-quality private container registry that’s straightforward to replicate.
You can skip everything and use the docker-compose file at the end of this blog post. 🙂
1. Create a Custom Network
First, ensure you have a dedicated Podman network (here called test
) for seamless container communication.
sudo podman network create test
2. Start the PostgreSQL Database
Quay relies on a PostgreSQL database. Start one with the following, customizing passwords as needed. If you’re using RHEL, CentOS, Fedora, you will likely need the command below to define the permissions required on the directory /docker/postgres-quay.
sudo mkdir -p /docker/postgres-quay
sudo setfacl -m u:26:-wx /docker/postgres-quay
sudo podman run -d --name postgresql-quay --net test \
-e POSTGRESQL_USER=quayuser1 \
-e POSTGRESQL_PASSWORD=quaypass1 \
-e POSTGRESQL_DATABASE=quay \
-e POSTGRESQL_ADMIN_PASSWORD=adminpass123 \
-p 5432:5432 \
-v /docker/postgres-quay:/var/lib/pgsql/data:Z \
registry.redhat.io/rhel8/postgresql-10:1
Next, enable the pg_trgm
extension (required for Quay):
sudo podman exec -it postgresql-quay /bin/bash -c 'echo "CREATE EXTENSION IF NOT EXISTS pg_trgm" | psql -d quay -U postgres'
3. Start the Redis Cache
Quay also uses Redis for caching and background tasks. Launch Redis as below:
sudo podman run -d --name redis --net test \
-p 6379:6379 \
-e REDIS_PASSWORD=strongpassword \
registry.redhat.io/rhel8/redis-5:1
4. Generate Quay Configuration
Before the Quay service itself can run, you need to create a config bundle. Run the Quay container in configuration mode:
sudo podman run --rm -it --name quay_config --net test \
-p 80:8080 -p 443:8443 \
-v /docker/quay/config:/conf/stack:Z \
registry.redhat.io/quay/quay-rhel8:v3.7.8 config secret
Follow the web config prompts in your browser (connect to http://localhost:80 or https://localhost:443 if running locally), set your registry settings, and fill out DB/Redis parameters.
In the web UI, enter the below details before generating the config file
- Basic configuration
- Don’t change, use default values.
- Server configuration
- enter quay.example.com
- Database
- For the purpose of this exercise, enter the below
- Database Type: Postgres
- Database Server: postgresql-quay:5432
- Username: quayuser1
- Password: quaypass1
- Database Name: quay
- Redis
- Redis Hostname: redis
- Redis port: 6379 (default)
- Redis password: strongpassword1
When all required fields have been set, validate your settings by clicking Validate Configuration Changes. If any errors are reported, continue editing your configuration until all required fields are valid and Red Hat Quay can connect to your database and Redis servers.
5. Run the Quay Service
Now launch your Quay service using the generated config and a persistent data path:
sudo podman run -d --stop-timeout=30 -p 80:8080 -p 443:8443 \
--name=quay --net test \
-v /docker/quay/config:/conf/stack:Z \
-v /docker/quay/storage:/datastorage:Z \
registry.redhat.io/quay/quay-rhel8:v3.7.8
6. OPTIONAL: Using Docker Instead of Podman
If you prefer Docker, the commands are almost identical except for the syntax:
docker run --rm -it --name quay_config --net test \
-p 80:8080 -p 443:8443 \
-v /docker/quay/config:/conf/stack:Z \
registry.redhat.io/quay/quay-rhel8:v3.7.8 config secret
docker run -d --stop-timeout=30 -p 8080:8080 -p 8443:8443 \
--name=quay --net test \
-v /docker/quay/config:/conf/stack:Z \
-v /docker/quay/storage:/datastorage:Z \
registry.redhat.io/quay/quay-rhel8:v3.7.8
7. Secure With Nginx, Let’s Encrypt, and Cloudflare
By default, Podman/Docker containers may use self-signed certs, prompting browser and CLI warnings. To remedy this:
- Set up nginx as a reverse proxy in front of your Quay service.
- Use Certbot (Let’s Encrypt) to generate trusted SSL certificates.
- Optionally, configure Cloudflare as your DNS proxy for added DNS-layer protection and to streamline SSL management.
Basic example Nginx config snippet:
server {
listen 443 ssl;
server_name yourregistry.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourregistry.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourregistry.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
After reloading nginx, your Quay registry should be accessible at https://quay.example.com
with a valid SSL certificate—no more invalid cert warnings when using Podman, Docker, or via browsers.
Tip: Always keep your configuration files and storage directories secure and regularly backed up. Consider automating cert renewals with Certbot’s cron setup.
That’s it! You now have a secure, production-ready Quay registry running in containers, backed by PostgreSQL and Redis, and fully protected by SSL via Nginx and Let’s Encrypt behind Cloudflare.
8. Kickstart with docker-compose
I’m going to paste my docker-compose.yml here for easy access infuture.
services:
nginx-pm:
container_name: nginx-pm
image: "jlesage/nginx-proxy-manager:latest"
restart: unless-stopped
ports:
- '443:4443'
# - '81:81'
- '80:8080'
volumes:
- "/docker/nginx-pm:/config:rw"
environment:
- "USER_ID=xXx"
- "GROUP_ID=xXx"
- "TZ=Asia/Singapore"
- "DISABLE_IPV6=1"
networks:
test:
redis:
container_name: redis
image: registry.redhat.io/rhel8/redis-5:1
environment:
- REDIS_PASSWORD=strongpassword1
networks:
test:
postgres-quay:
container_name: postgresql-quay
image: registry.redhat.io/rhel8/postgresql-10:1
volumes:
- /docker/postgres-quay:/var/lib/pgsql/data
environment:
- POSTGRESQL_USER=quayuser1
- POSTGRESQL_PASSWORD=quaypass1
- POSTGRESQL_DATABASE=quay
- POSTGRESQL_ADMIN_PASSWORD=adminpass123
networks:
test:
quay:
container_name: quay
image: "registry.redhat.io/quay/quay-rhel8:v3.7.8"
restart: unless-stopped
#ports:
# - 8080:8080
# - 8443:8443
volumes:
- /docker/quay/config:/conf/stack:Z
- /docker/quay/storage:/datastorage:Z
stop_grace_period: 30s
networks:
test:
networks:
test:
name: test
external: true