Skip to content

Custom applications

A custom application replaces the file listing of your site with an application that you provide as a container image. The platform runs your image inside the site pod, next to the platform components. Your application shares the site storage and answers requests under your site URL.

Set up a custom application in the Hub: open your site, select Settings > Application Options > Custom Application Deployment, enter the image, and save. The site restarts. A large image makes the restart slower.

The image field always needs a full reference: the registry, the complete path, the image name, and a tag or a @sha256:<digest> reference. A short Docker Hub name such as redis:7 is not a full reference; write docker.io/library/redis:7. Your deployment can also limit the registries it accepts. The form shows an example of a reference it accepts.

What your image must do

Your image must meet these requirements:

  • Listen on port 8000. The platform also sets the PORT environment variable to 8000. Listen on 0.0.0.0, not on 127.0.0.1. The site proxy connects to your application on 127.0.0.1, but the platform health checks connect to the pod address. If your application listens only on 127.0.0.1, the health checks fail. The site then stays in preparing, and the platform restarts your container.
  • Do not use the reserved ports. All containers of the site share one network. The platform applications use ports 4180, 6000, 7000, 8080, 9000, and 10000. Your application, and each process in your image, must not listen on these ports. If a process uses one of these ports, a platform application cannot start, and the site does not work correctly.
  • Speak HTTP or FastCGI. Choose the matching Proxy mode in the form: HTTP proxy for HTTP applications, FastCGI for FastCGI applications such as PHP-FPM.
  • Run without root privileges. The platform starts the container as a non-root user and does not allow privilege escalation.
  • Write only where needed. The container filesystem is writable, but the platform does not keep its content across restarts. The site storage is mounted at /www; published files are visible there, and your application can write there too.
  • Accept traffic only from the site proxy. The application has no service and no external route. The platform performs access control before it forwards a request to your application.

The container receives the standard site environment variables, including MASTER_TOKEN and SITE_PATH. Do not define variables with these names in the Environment Variables field: the platform rejects reserved names. Each custom variable must be a NAME=VALUE entry, and a value cannot contain a comma.

The Environment Variables editor shows one row per variable, with an Add variable control. The control creates a new row only when every existing row is complete; otherwise the editor moves the cursor to the field you left empty. A variable needs a name; its value can be empty (NAME=). The site page lists the variable names only; open the dialog to read the values, which stay masked until you choose Show values. The platform stores these values as part of the site configuration, not in a credential store: every site administrator can read them, and platform backups include them. Applications that need a secret usually read it from a file in their own storage, or from a configuration service; see Publishing content.

Where your application answers

Choose the URL path setting to decide which part of the URL reaches your application:

Setting Your application sees The URL visitors use
Site path prefix Requests for the full site path https://<host>/<space>/<site>/...
Root (/) Requests at / https://<host>/<space>/<site>/...

With Site path prefix, your application receives the site path in the request, and links in your pages can stay relative. With Root (/), the platform forwards /... to your application, and your application is mounted at the root of the upstream. The FastCGI mode always sends the full request path to the application. Understanding Sites URLs compares the two settings with examples.

The platform keeps working with your site: it adds the retention banner to HTML responses, and it still serves the site with the access rules you set.

Example: a small HTTP application

app.py with Flask:

import os
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return {'message': 'Hello from my custom application'}

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 8000)))

Dockerfile:

FROM python:3.13-slim
RUN useradd -r -u 10001 appuser
WORKDIR /app
COPY app.py requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
USER appuser
CMD ["python", "app.py"]

Build it, push it to a registry your deployment allows, then enter the full image reference in the form, for example eccr.ecmwf.int/my-project/my-app:1.0.

Example: a FastCGI application

A PHP-FPM application must listen on port 8000, in TCP mode:

; www.conf
listen = 8000

Set Proxy mode to FastCGI. The platform passes the standard FastCGI parameters. When you set DOCUMENT_ROOT in the Environment Variables field, the platform uses it for SCRIPT_FILENAME and DOCUMENT_ROOT.

Choosing an image reference

  • Enter the full reference: registry host, project, image name, and a tag or a digest. Short names such as redis:7 are not valid.
  • Pin what matters: with a floating tag, the platform pulls the newest image with that tag at every restart. A fixed tag gives you control; an @sha256: digest gives you reproducibility.
  • Your deployment can restrict the registries you may use. The form tells you the allowed list when it rejects an image.
  • The Hub does not check that the image exists in the registry. When the platform cannot pull the image, the status line on the Settings tab shows error after the restart, and the System tab shows the details.

Restrictions

  • Private sites only, by default. A site with a custom application must be private. If your deployment allows custom applications on public sites, you can keep the site public. Otherwise, the Hub rejects a new or changed image on a public site: make the site private first. When a public site already has an image, your next change to that site makes it private. The Hub asks you to confirm this change, and the platform adds the default access users and groups when you have none.
  • Read-only methods on a public site. A public site answers GET, HEAD, OPTIONS, PROPFIND, and SEARCH only. A POST, PUT, PATCH, or DELETE answers 403 before your application receives it, also when your deployment allows a custom application on a public site. A token or a cookie does not change this. When your application can put its input in the URL, use GET. Use a private site when your application must accept a request body.
  • Allowed registries. A platform administrator can limit the image to a list of registries. Ask your platform team when the form rejects your registry.
  • Private registries need a pull Secret. When your registry needs credentials, a platform administrator must make the pull Secret available in the site namespace. Without it, the site shows ImagePullBackOff on the System tab.

Removing a custom application

Select the X button next to the image field, or clear the field by hand, then save. The X button only empties the image field: it saves nothing, and the other fields keep their values until you save. The site restarts and serves the normal file listing again. This also applies when your application does not start or does not accept connections on port 8000: the platform replaces the failing container. The platform then discards the proxy mode, the URL path, and the environment variables of the custom application.

If the site does not start

Open the System tab and read the Site Status card. It shows the failing container, a short label, and the raw reason in brackets. Select Load logs to read the logs of that container.

Reason Cause Fix
ImagePullBackOff, ErrImagePull The image does not exist, or the cluster cannot pull it Check the spelling of the reference, the tag, and the registry access
CrashLoopBackOff Your application exits at start-up Check the logs of the site-custom container
OOMKilled Your application used more memory than its limit Reduce the memory your application uses, or ask your platform team for a higher resource limit
NotReady on the System tab, or the label waiting for the application next to preparing Your application runs, but it does not accept connections on port 8000 yet. This is normal during start-up. When it continues, the possible causes are: the application listens on 127.0.0.1 or on a port other than 8000, the start-up is slow, or the application has an error Your application must listen on 0.0.0.0:8000. Read the logs of the site-custom container to find the cause
CreateContainerConfigError A platform problem with the site resources Contact the support team

A pod that passes its TCP health checks but does not answer on HTTP usually speaks the wrong protocol for the proxy mode you chose. An application that listens on the wrong interface (127.0.0.1 instead of 0.0.0.0) or on the wrong port fails the TCP health checks; the NotReady row above applies.

The Settings tab shows the same state in one line under the image field: preparing, running, or error. The line says preparing while the platform pulls the image, starts the container, or waits for your application to accept connections on port 8000; in the last case the label waiting for the application shows, and the line also shows Restart now. This state is normal while your application starts. When it continues, read the site-custom logs to find the cause. While your application does not accept connections, the site's s/ services keep working: the Site API, WebDAV, and S3 answer as usual. Every other path under the site URL belongs to your application, so requests to them fail. On error, the line names the problem — cannot pull image, application failed, or out of memory — and pointing at the label shows one sentence on what to do. The badge tooltip carries the container message. Choose Restart now to restart the site without leaving the tab. Use it when you fixed the problem by pushing a new image under the same tag: nothing on the site changed, so the platform does not restart anything on its own. Changing the image reference itself already restarts the site.