Skip to content

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):
sudo dnf install wget -y
sudo dnf install nano -y
sudo dnf install git -y

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".

Rahti gui

  • Go back to your VM. Download OpenShift CLI:
wget https://<downloadlinkhere>/something/oc.tar
  • Unzip the tar file.
tar -xvf oc.tar
  • Add the extracted oc executable to path. First, check your path with
    pwd
    

Check path in vm

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

Rahti gui

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

Check oc

  • 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".

Get 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.

Display token

  • Run the given command in your virtual machine

Log in from cli

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.

Check 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:

New project created

  • Check, that your project was created and you are using it.
oc projects

oc projects

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

Rahti has new project

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

oc apply -f <yourtemplatename>.yaml
Deploy Status

  • See the status of your pods
    oc get pods -w
    
    Pod Status
  • See the status of your PrestaShop installation
    oc logs -f <POD-NAME>
    

Installation Status

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

PrestaShop

PrestaShop

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

Rahti gui

  • "Workloads" tab should have PrestaShop and database.

Rahti gui

  • If you want to delete your app, nodes, pods or pvc, you can use oc delete command. For example, delete the app with:

oc delete -f <youtemplatename>.yaml
or
oc delete all -l app=prestashop

  • You can also delete everything from Rahti web interface.