Deploying PrestaShop on Rahti#
| Original author | Sara Jokela |
| Updated by | Tuukka Peltomäki |
| Status | In progress |
| Last updated | 25.9.2025 |
Notes#
Prerequisites#
- Rocky Linux virtual machine in CSC Pouta (without any PrestaShop installation). Check the installation guide.
- Clone PrestaShop Service-X project to your virtual machine.
Step 1: Prepare virtual machine#
- Install necessary tools (wget, nano, git):
Step 2: Install OpenShift CLI#
- In order to use Rahti from CLI, an OpenShift CLI tool has to be installed
- You can find a download link from your Rahti profile. Log in to your Rahti project on https://console.rahti.csc.fi.
- Click the question mark next to your name. Choose "Command Line Tools". Hover your mouse over "Download oc for Linux for x86_64". Right-click with your mouse, and select "Copy link".

- Go back to your VM. Download OpenShift CLI:
- Unzip the tar file.
- Add the extracted oc executable to path. First, check your path with

- Open .bashrc to add path
- Add path to file, save and exit. Note, that you probably have to restart your virtual machine to apply this change!

- Type oc to cli to see that the command works.

- Now that OpenShift CLI works, let's use it to login to our Rahti. Go to your Rahti project. Once again, click the question mark next to your name, and select "Command Line Tools". Now, click "Copy login command".

- A new tab will open and prompt you to log in with your student credits. After logging in, you should see a page with "Display Token" written on it. Click it to see commands that you can use to log in from VM.

- Run the given command in your virtual machine

Step 3: Create a project in Rahti#
- In order to create a project in Rahti, you need to know your project number. Go to your account on my.csc.fi. Select your Rahti project, and check the Project number.

- Go back to your virtual machine. Run command
oc new-project <giveanewprojectnamehere> --description="csc_project: <thatRahtiprojectnumber>" --display-name="givesomenamehere"
- You should see following text:

- Check, that your project was created and you are using it.

- Go to your Rahti web interface. Your project should have appeared there as well.

Stage 4: Deploying PrestaShop#
NOTE#
- This installation uses forked versions of PrestaShop service-container and related projects (base-image, presta-shop-code and database-container). Please do not use those projects straight from ref-product-line-v1-2025, fork them to your own repository.
- OpenShift HTTP and HTTPS ports must be 8080 / 8443, since OpenShift Enterprise operations have reserved ports up to 1024.
- On Rahti (OpenShift), containers do not run as a fixed user like root or www-data. Instead, OpenShift assigns a random user ID (UID) at runtime.
This has two important consequences:
- The container cannot rely on hardcoded ownership like chown www-data:www-data.
- Directories and files must be writable by a group, and the random UID must belong to that group (root is always included).
Therefore, we must:
- Remove chown commands that set ownership to fixed users (www-data, mysql, etc.).
- Add chgrp 0 and chmod g+rwX so any OpenShift UID in group 0 can still write.
- Disable gosu or runuser, since OpenShift prevents user switching.
1. Change privileges
- base-image/Dockerfile
# TRUNCATED # ... # PHP env for dev / demo modes COPY config_files/defines_custom.inc.php /tmp/ # REMOVE # chown fixes ownership to www-data, which will break on OpenShift because containers run with a random UID #RUN chown www-data:www-data /tmp/defines_custom.inc.php # # ADD # Expose port 8080, OpenShift Enterprise operations have reserved ports up to 1024 # Apache → port 8080 RUN a2enmod rewrite \ && sed -i 's/80/8080/g' /etc/apache2/ports.conf /etc/apache2/sites-available/*.conf EXPOSE 8080 # # Apache configuration RUN if [ -x "$(command -v apache2-foreground)" ]; then a2enmod rewrite; fi # PHP configuration COPY config_files/php.ini /usr/local/etc/php/ # Allow exection for entrypoint RUN ["chmod", "+x", "/tmp/docker_run.sh"] # ADD # Makes files writable by the OpenShift-assigned UID, since it belongs to group 0 RUN chgrp -R 0 /var/www/html /tmp \ && chmod -R g+rwX /var/www/html /tmp \ && chmod +x /tmp/docker_run.sh # # Run CMD ["/tmp/docker_run.sh"] # For Debugging purposes only # CMD ["sleep","3600"]
- base-image/config_files/docker_run.sh
# TRUNCATED
# ...
if [ $PS_INSTALL_AUTO = 1 ]; then
echo "\n* Installing PrestaShop, this may take a while ...";
if [ "$PS_DOMAIN" = "<to be defined>" ]; then
export PS_DOMAIN=$(hostname -i)
fi
echo "\n* Launching the installer script..."
# REMOVE
# Switching users with runuser is not allowed under OpenShift security restrictions
#runuser -g www-data -u www-data -- php -d memory_limit=-1 /var/www/html/$PS_FOLDER_INSTALL/index_cli.php \
#--domain="$PS_DOMAIN" --db_server=$DB_SERVER:$DB_PORT --db_name="$DB_NAME" --db_user=$DB_USER \
#--db_password=$DB_PASSWD --prefix="$DB_PREFIX" --firstname="John" --lastname="Doe" \
#--password=$ADMIN_PASSWD --email="$ADMIN_MAIL" --language=$PS_LANGUAGE --country=$PS_COUNTRY \
#--all_languages=$PS_ALL_LANGUAGES --newsletter=0 --send_email=0 --ssl=$PS_ENABLE_SSL
#
# ADD
# Runs the installer directly as the current OpenShift user
php -d memory_limit=-1 /var/www/html/$PS_FOLDER_INSTALL/index_cli.php \
--domain="$PS_DOMAIN" --db_server=$DB_SERVER:$DB_PORT --db_name="$DB_NAME" --db_user=$DB_USER \
--db_password=$DB_PASSWD --prefix="$DB_PREFIX" --firstname="John" --lastname="Doe" \
--password=$ADMIN_PASSWD --email="$ADMIN_MAIL" --language=$PS_LANGUAGE --country=$PS_COUNTRY \
--all_languages=$PS_ALL_LANGUAGES --newsletter=0 --send_email=0 --ssl=$PS_ENABLE_SSL
#
if [ $? -ne 0 ]; then
echo 'warning: PrestaShop installation failed.'
else
echo "\n* Removing install folder..."
rm -r /var/www/html/$PS_FOLDER_INSTALL/
fi
fi
# ...
# TRUNCATED
- service-container/Dockerfile
# TRUNCATED
# ...
RUN mkdir -p /tmp/data-ps
RUN unzip -q /tmp/prestashop.zip -d /tmp/
# Rename of original repository name
RUN mv /tmp/ref-product-presta-shop-code-v1-main /tmp/prestashop
RUN bash /tmp/ps-extractor.sh /tmp/
RUN rm /tmp/prestashop.zip
# REMOVE
# Fixed ownership to www-data breaks on OpenShift
# Fix for missing logs folder
#RUN mkdir /var/www/html/var/logs # REMOVE
#RUN chown www-data:www-data /var/www/html/var/logs
#
# ADD
# Ensure logs folder exists. Remove leftover lock files to prevent blocked installations. Grant group write permissions so OpenShift UID can write.
RUN mkdir -p /var/www/html/var/logs \
&& rm -f /var/www/html/install.lock /var/www/html/install/install.lock \ # ADD
&& chgrp -R 0 /var/www/html /tmp/prestashop \
&& chmod -R g+rwX /var/www/html /tmp/prestashop
#
- database-container/Dockerfile
# SOURCE: https://github.com/docker-library/mysql/tree/master/8.0 # NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" # # PLEASE DO NOT EDIT IT DIRECTLY. # FROM debian:bookworm-slim # add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added RUN groupadd -r mysql && useradd -r -g mysql mysql RUN apt-get update && apt-get install -y --no-install-recommends gnupg && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get install -y dos2unix # REMOVE # Remove this. gosu (like su) not allowed in OpenShift # add gosu for easy step-down from root # https://github.com/tianon/gosu/releases #ENV GOSU_VERSION 1.17 #RUN set -eux; \ # # savedAptMark="$(apt-mark showmanual)"; \ # apt-get update; \ # apt-get install -y --no-install-recommends ca-certificates wget; \ # rm -rf /var/lib/apt/lists/*; \ # dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ # wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ # wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ # export GNUPGHOME="$(mktemp -d)"; \ # gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ # gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ # gpgconf --kill all; \ # rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ # apt-mark auto '.*' > /dev/null; \ # [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ # apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ # chmod +x /usr/local/bin/gosu; \ # gosu --version; \ # gosu nobody true # RUN mkdir /docker-entrypoint-initdb.d RUN set -eux; \ apt-get update; \ apt-get install -y --no-install-recommends \ bzip2 \ openssl \ # FATAL ERROR: please install the following Perl modules before executing /usr/local/mysql/scripts/mysql_install_db: # File::Basename # File::Copy # Sys::Hostname # Data::Dumper perl \ xz-utils \ zstd \ ; \ rm -rf /var/lib/apt/lists/* RUN set -eux; \ # pub rsa4096 2023-10-23 [SC] [expires: 2025-10-22] # BCA4 3417 C3B4 85DD 128E C6D4 B7B3 B788 A8D3 785C # uid [ unknown] MySQL Release Engineering <mysql-build@oss.oracle.com> # sub rsa4096 2023-10-23 [E] [expires: 2025-10-22] key='BCA4 3417 C3B4 85DD 128E C6D4 B7B3 B788 A8D3 785C'; \ export GNUPGHOME="$(mktemp -d)"; \ gpg --batch --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys "$key"; \ mkdir -p /etc/apt/keyrings; \ gpg --batch --export "$key" > /etc/apt/keyrings/mysql.gpg; \ gpgconf --kill all; \ rm -rf "$GNUPGHOME" # If you get error that mysql-community-client and mysql-community-server-core was not found # Go this website https://github.com/docker-library/mysql/tree/master/8.0 and select Dockerfile.debian # Find out which version they updated to and replace MYSQL_VERSION with that ENV MYSQL_MAJOR 8.0 ENV MYSQL_VERSION 8.0.43-1debian12 RUN echo 'deb [ signed-by=/etc/apt/keyrings/mysql.gpg ] http://repo.mysql.com/apt/debian/ bookworm mysql-8.0' > /etc/apt/sources.list.d/mysql.list # the "/var/lib/mysql" stuff here is because the mysql-server postinst doesn't have an explicit way to disable the mysql_install_db codepath besides having a database already "configured" (ie, stuff in /var/lib/mysql/mysql) # also, we set debconf keys to make APT a little quieter RUN { \ echo mysql-community-server mysql-community-server/data-dir select ''; \ echo mysql-community-server mysql-community-server/root-pass password ''; \ echo mysql-community-server mysql-community-server/re-root-pass password ''; \ echo mysql-community-server mysql-community-server/remove-test-db select false; \ } | debconf-set-selections \ && apt-get update \ && apt-get install -y \ mysql-community-client="${MYSQL_VERSION}" \ mysql-community-server-core="${MYSQL_VERSION}" \ && rm -rf /var/lib/apt/lists/* \ # REMOVE # chown mysql:mysql assumes fixed UID, incompatible with OpenShift’s random UID. # && rm -rf /var/lib/mysql && mkdir -p /var/lib/mysql /var/run/mysqld \ # && chown -R mysql:mysql /var/lib/mysql /var/run/mysqld \ # # ensure that /var/run/mysqld (used for socket and lock files) is writable regardless of the UID our mysqld instance ends up having at runtime # && chmod 1777 /var/run/mysqld /var/lib/mysql # # ADD # Ensures database directories are writable by OpenShift UID && rm -rf /var/lib/mysql && mkdir -p /var/lib/mysql /var/run/mysqld \ && chgrp -R 0 /var/lib/mysql /var/run/mysqld \ && chmod -R g+rwX /var/lib/mysql /var/run/mysqld # VOLUME /var/lib/mysql # ADD # Makes init scripts usable under OpenShift UID RUN chgrp -R 0 /docker-entrypoint-initdb.d \ && chmod -R g+rwX /docker-entrypoint-initdb.d # # Config files COPY config/ /etc/mysql/ COPY docker-entrypoint.sh /usr/local/bin/ RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat RUN dos2unix /usr/local/bin/docker-entrypoint.sh && chmod +x /usr/local/bin/docker-entrypoint.sh ENTRYPOINT ["docker-entrypoint.sh"] EXPOSE 3306 33060 CMD ["mysqld"]
- database-container/docker-entrypoint.sh
# TRUNCATED # ... _main() { # if command starts with an option, prepend mysqld if [ "${1:0:1}" = '-' ]; then set -- mysqld "$@" fi # skip setup if they aren't running mysqld or want an option that stops mysqld if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." mysql_check_config "$@" # Load various environment variables docker_setup_env "$@" docker_create_db_directories "$@" # REMOVE # OpenShift blocks user switching — we must not drop privileges manually # If container is started as root user, restart as dedicated mysql user #if [ "$(id -u)" = "0" ]; then # mysql_note "Switching to dedicated user 'mysql'" # exec gosu mysql "$BASH_SOURCE" "$@" #fi # # ADD # Documents that the script now runs directly under the OpenShift-assigned UID # OpenShift: no user switching, run directly mysql_note "Running as current user (OpenShift random UID)" # # there's no database, so it needs to be initialized if [ -z "$DATABASE_ALREADY_EXISTS" ]; then docker_verify_minimum_env # check dir permissions to reduce likelihood of half-initialized database ls /docker-entrypoint-initdb.d/ > /dev/null docker_init_database_dir "$@" mysql_note "Starting temporary server" docker_temp_server_start "$@" mysql_note "Temporary server started." mysql_socket_fix docker_setup_db docker_process_init_files /docker-entrypoint-initdb.d/* mysql_expire_root_user mysql_note "Stopping temporary server" docker_temp_server_stop mysql_note "Temporary server stopped" echo mysql_note "MySQL init process done. Ready for start up." echo else mysql_socket_fix fi fi exec "$@" } # If we are sourced from elsewhere, don't perform any further actions if ! _is_sourced; then _main "$@" fi
2. Create a Docker registry secret on the server (inside your cPouta VM)
oc create secret docker-registry gitlab-registry-secret \
--docker-server=gitlab.labranet.jamk.fi:4567 \
--docker-username=<USERNAME> \
--docker-password=<PERSONAL-ACCESS-TOKEN> \
--docker-email=<EMAIL>
3. Create a deployment manifest on the server (inside your cPouta VM)
This manifest deploys PrestaShop on OpenShift using persistent volumes for data storage, and exposes it over HTTPS via an OpenShift Route with TLS termination.
Change these to your own information.
- prestashop-deployment.yaml
# Persistent volume for Prestashop application files - apiVersion: v1 kind: PersistentVolumeClaim metadata: name: prestashop-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi # Persistent volume for MySQL database storage - apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi # MySQL Service (exposes DB internally for Prestashop to connect) - apiVersion: v1 kind: Service metadata: name: prestashop-db spec: ports: - port: 3306 selector: app: prestashop-db # MySQL Deployment (backing database for Prestashop) - apiVersion: apps/v1 kind: Deployment metadata: name: prestashop-db spec: replicas: 1 selector: matchLabels: app: prestashop-db template: metadata: labels: app: prestashop-db spec: containers: - name: database image: <YOUR-DATABASE-IMAGE> # MariaDB/MySQL image with OpenShift adjustments env: - name: MYSQL_ROOT_PASSWORD # Root password (use secret in production) value: rootpassword - name: MYSQL_DATABASE # PrestaShop DB name value: prestashop - name: MYSQL_USER # PrestaShop DB user value: prestashop - name: MYSQL_PASSWORD value: password volumeMounts: - name: mysql-data mountPath: /var/lib/mysql # Persist database data - name: mysql-run mountPath: /var/run/mysqld # Ephemeral runtime files - name: mysql-files mountPath: /var/lib/mysql-files # Ephemeral init files securityContext: # Drop root-like privileges allowPrivilegeEscalation: false capabilities: drop: ["ALL"] seccompProfile: type: RuntimeDefault volumes: - name: mysql-data persistentVolumeClaim: claimName: mysql-pvc - name: mysql-run emptyDir: {} # Temporary runtime dir (not persisted) - name: mysql-files emptyDir: {} # Temporary init dir (not persisted) imagePullSecrets: - name: gitlab-registry-secret # Prestashop Service (exposes app internally) - apiVersion: v1 kind: Service metadata: name: prestashop-service spec: selector: app: prestashop ports: - protocol: TCP port: 80 targetPort: 8080 type: ClusterIP # Prestashop Deployment (main application) - apiVersion: apps/v1 kind: Deployment metadata: name: prestashop spec: replicas: 1 selector: matchLabels: app: prestashop template: metadata: labels: app: prestashop spec: initContainers: - name: fix-prestashop-lock # Clean up lock files from previous installs image: busybox:latest command: ["sh", "-c", "rm -f /var/www/html/install.lock /var/www/html/install/install.lock /var/www/html/app/config/*.lock"] volumeMounts: - name: prestashop-data mountPath: /var/www/html securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] seccompProfile: type: RuntimeDefault - name: copy-prestashop-files # Copy Prestashop files into mounted volume image: gitlab.labranet.jamk.fi:4567/presta-test/ref-product-presta-shop-service-container-v1:rahti-latest command: ["sh", "-c", "cp -r /tmp/prestashop/* /var/www/html/"] volumeMounts: - name: prestashop-data mountPath: /var/www/html securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] seccompProfile: type: RuntimeDefault containers: - name: prestashop image: <YOUR-SERVICE-CONTAINER-IMAGE> ports: - containerPort: 80 env: - name: DB_SERVER value: "prestashop-db" - name: DB_USER value: "prestashop" - name: DB_PASSWD value: "password" - name: DB_NAME value: "prestashop" - name: PS_INSTALL_AUTO # Auto-install Prestashop on startup value: "1" - name: PS_DOMAIN # Domain provided by Rahti value: <PRESTASHOP-DOMAIN-FROM-RAHTI> # e.g. "prestashop-prestatest.2.rahtiapp.fi" - name: PS_LANGUAGE value: "fi" - name: PS_FOLDER_ADMIN # Custom admin folder name (security by obscurity) value: "admin32sfcxe2" - name: PS_ENABLE_SSL value: "1" - name: ADMIN_MAIL # Initial admin email value: <EMAIL> - name: ADMIN_PASSWD # Initial admin password value: "password" securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] seccompProfile: type: RuntimeDefault volumeMounts: - name: prestashop-data mountPath: /var/www/html volumes: - name: prestashop-data persistentVolumeClaim: claimName: prestashop-pvc imagePullSecrets: - name: gitlab-registry-secret # Openshift Route (external HTTPS endpoint) - apiVersion: route.openshift.io/v1 kind: Route metadata: name: prestashop spec: to: kind: Service name: prestashop-service port: targetPort: 8080 tls: termination: edge # TLS handled by OpenShift router
- Save and exit.
- Now you should be able to deploy it with
- See the status of your pods

- See the status of your PrestaShop installation

- After the installation is finished, you should be able to access PrestaShop


- After a while, you should see pods, services, persistent volume etc. in your Rahti web interface.

- "Workloads" tab should have PrestaShop and database.

- If you want to delete your app, nodes, pods or pvc, you can use
oc deletecommand. For example, delete the app with:
- You can also delete everything from Rahti web interface.