I usually prefer to host Quay using the Operator on OpenShift. It's clean, declarative, and integrates perfectly. But sometimes, that OpenShift Cluster is just not available yet, and you need a place to put your images right now. In those situations, you have to improvise and host the initial registry on a standalone virtual machine.
Here is how you can spin up a quick, containerized Quay Proof of Concept (PoC) environment using Podman, PostgreSQL, and Redis.
First, we need to create some directories that we're going to use for the different backing services that Quay requires. We'll set these up under '/var/podman':
[archy@quay ~]$ mkdir -p -m 755 /var/podman/{postgresql,redis,quay}
[archy@quay ~]$ mkdir -p -m 755 /var/podman/redis/{config,data}
[archy@quay ~]$ mkdir -p -m 755 /var/podman/quay/{config,storage}
Next, we need to set some ACLs so that the PostgreSQL and Quay containers can write to their respective storage locations. Without this, you will run into permission denied errors due to user namespace mapping:
[archy@quay ~]$ setfacl -m u:26:-wx /var/podman/postgresql
[archy@quay ~]$ setfacl -m u:1001:-wx /var/podman/quay/storage
Since I have a second disk ('/dev/vdb') in my VM that I'm dedicating to Podman and PostgreSQL storage, we need to set that up. I'm using LVM and XFS here, and making sure the mounts persist across reboots by adding them to '/etc/fstab':
[archy@quay ~]$ pvcreate /dev/vdb
[archy@quay ~]$ vgcreate vg_storage /dev/vdb
[archy@quay ~]$ lvcreate -n 'lv_var_podman_postgresql' -L 8G /dev/vg_storage
[archy@quay ~]$ lvcreate -n 'lv_var_podman_quay' -L 8G /dev/vg_storage
[archy@quay ~]$ mkfs.xfs /dev/vg_storage/lv_var_podman_postgresql
[archy@quay ~]$ mkfs.xfs /dev/vg_storage/lv_var_podman_quay
[archy@quay ~]$ cat << EOF >> /etc/fstab
/dev/mapper/vg_storage-lv_var_podman_postgresql /var/podman/postgresql xfs defaults 0 0
/dev/mapper/vg_storage-lv_var_podman_quay /var/podman/quay xfs defaults 0 0
EOF
Now we need to install Podman alongside some useful container tools like 'buildah' and 'skopeo'. We also want to confine Quay and its backing services to their own dedicated bridge network so they can resolve each other by container name.
[archy@quay ~]$ dnf -y --refresh install container-tools podman buildah skopeo
[archy@quay ~]$ podman network create -d 'bridge' quay
Quay requires a database. We'll spin up a PostgreSQL 16 container, attach it to our network, and map it to the persistent storage we just created.
[archy@quay ~]$ podman run -d -v '/var/podman/postgresql:/var/lib/postgresql/data:rw,z' -p '5432:5432' -e 'POSTGRES_DB=quay' -e 'POSTGRES_USER=quay' -e 'POSTGRES_PASSWORD=<random-and-generated-postgresql-password>' --rm --name 'postgresql' --network 'quay' --tz 'Europe/Prague' docker.io/library/postgres:16.14-trixie
Quay also specifically requires the 'pg_trgm' extension to be enabled in PostgreSQL to handle searching properly. We can inject this right into the running container:
[archy@quay ~]$ podman exec -it postgresql /bin/bash -c 'echo -e "CREATE EXTENSION IF NOT EXISTS pg_trgm;" | psql -d "quay" -U "quay"'
Next up is Redis, which Quay uses for user events and build logs. First, generate the configuration file:
[archy@quay ~]$ cat << EOF > /var/podman/redis/config/redis.conf
bind 0.0.0.0
protected-mode no
port 6379
maxmemory 512mb
maxmemory-policy allkeys-lru
appendonly yes
loglevel notice
requirepass <random-and-generated-redis-password>
EOF
Then, fire up the Redis container:
[archy@quay ~]$ podman run -d -v '/var/podman/redis/config/redis.conf:/usr/local/etc/redis/redis.conf:ro,z' -v '/var/podman/redis/data:/data:rw,z' -p '6379:6379' -e 'REDIS_PASSWORD=<random-and-generated-redis-password>' --rm --name 'redis' --network 'quay' --tz 'Europe/Prague' docker.io/library/redis:8.4.4-trixie
Now for the main event. First, we need to create the 'config.yaml' file for Quay. Make sure to update the passwords and secret keys to match what you generated for the backing services.
[archy@quay ~]$ cat << EOF > /var/podman/quay/config/config.yaml
AUTHENTICATION_TYPE: Database
BUILDLOGS_REDIS:
host: redis
password: <random-and-generated-redis-password>
port: 6379
ssl: false
CREATE_NAMESPACE_ON_PUSH: true
DATABASE_SECRET_KEY: <random-and-generated-database-secret-key>
DB_URI: postgresql://quay:<random-and-generated-postgresql-password>@postgresql:5432/quay
DISTRIBUTED_STORAGE_CONFIG:
default:
- LocalStorage
- storage_path: /datastorage/registry
DISTRIBUTED_STORAGE_DEFAULT_LOCATIONS: []
DISTRIBUTED_STORAGE_PREFERENCE:
- default
FEATURE_MAILING: false
SECRET_KEY: <random-and-generated-secret-key>
SERVER_HOSTNAME: quay01.archyslife.lan
SETUP_COMPLETE: true
USER_EVENTS_REDIS:
host: redis
password: <random-and-generated-redis-password>
port: 6379
ssl: false
EOF
Finally, launch the Quay container itself. We are mapping ports '80' and '443' to the container's internal '8080' and '8443' ports.
[archy@quay ~]$ podman run -d -v '/var/podman/quay/config:/conf/stack:rw,z' -v '/var/podman/quay/storage:/datastorage:rw,z' -p '80:8080' -p '443:8443' --name 'quay' --network 'quay' --tz 'Europe/Prague' quay.io/projectquay/quay:v3.17.3
Give it a few moments to spin up, initialize the database schemas, and start serving traffic. You can monitor the progress by following the container logs:
[archy@quay ~]$ podman logs -f quay
Once the setup is complete and the application is running, you should be able to navigate to the hostname you set in your quay's 'config.yaml' in your browser and log into your new standalone registry.
Feel free to comment and / or suggest a topic.

Comments
Post a Comment