Using custom startup commands with Web App for Containers

6 minute read | By Anthony Salemo

This post will cover how to use the “Startup Command” option to quickly change the startup entrypoint or command for your custom container on Web Apps for Containers.

Overview

The “Startup Command” option is available on both App Service Blessed Images and Web App for Containers - however, there are some important differences here.

“Blessed Images” are prebuilt containers which mount a volume to /home to run your code. The Dockerfile used for these images are configured in a way that this command can easily integrate with your code, since the architecture here is designed to just deploy and run code and not focus on the nuances of configuring the image itself.

On the other hand, Web App for Containers have the expectation set that the customer would be building their own custom Docker Image - and bringing that for Azure to run as a container. With this, most people have an already set and defined container entrypoint, through the ENTRYPOINT instruction, or the CMD command to run their application through CMD.

If you add a “Startup Command”, this may cause issues unknowingly, as it may override your command in the same way you can do docker run -it [someimage] [somecommand].

Good to knows

If your custom Docker Image already has a ENTRYPOINT or CMD set, setting this custom startup command option may break your application, as it’s essentially trying to override your entrypoint or command execution.

Take the below example. We have a quickstart nginx:latest image being used - which runs fine on it’s own. If we add a command of echo "Hello" as the Startup Command, we see it execute - but ultimately the container fails because it’s overriding NGINX’s entrypoint.

NGINX

Startup Command

Echo Command

Container Crash

Knowing this, we will go through a setup below to successfully use this option for a Web App for Container.

Custom Startup Commands

Use a command

  1. To be able to start a container with this method, do not have a CMD or ENTRYPOINT in your Dockerfile. See the below example. We’re just copying in our application source and exposing the port. If you have a CMD or ENTRYPOINT already set, there is a likely chance this may crash the container, as seen above, if you put a command that your application is not expecting.
FROM node:18.9.0-alpine3.15

WORKDIR /app
COPY package.json ./
RUN npm i

COPY . ./

EXPOSE 8080 
  1. In the “Startup Command” field, add your command.

Startup Command

  1. We can now validate the container has started:

Startup Command

This approach can be used an alternative to the typical ENTRYPOINT or CMD methods with containerized applications.

Use a file

Prerequisites

Compared to the Use a command section, to reference a .sh file directly to invoke your container entrypoint, you must have App Service persistant storage enabled. This is done through the WEBSITES_ENABLE_APP_SERVICE_STORAGE App Setting.

On Web App for Containers, this defaults to false. Set this to true to proceed, otherwise this will not work.

Startup Command

Set Up

  1. Assuming WEBSITES_ENABLE_APP_SERVICE_STORAGE is now true, we can continue. Next, using an FTP client of your choice - add your entrypoint container script to a location under /home.

    In this example, we’re going to add the below init_container.sh to /home/site/wwwroot (This is where FTP clients default to when connected under App Service remote storage)

     #!/bin/sh
     set -e
    
     # Get env vars in the Dockerfile to show up in the SSH session
     eval $(printenv | sed -n "s/^\([^=]\+\)=\(.*\)$/export \1=\2/p" | sed 's/"/\\\"/g' | sed '/=/s//="/' | sed 's/$/"/' >> /etc/profile)
    
     echo "Starting SSH ..."
     /usr/sbin/sshd
    
     node /app/server.js
    

    Remote Storage

    NOTE: It’s important to understand that FTP clients only connect to the remote storage’s file system. You cannot browse the container filesystem like you would do with an SSH session.

  2. Just as in the “Use a command” section - do not have an existing ENTRYPOINT or CMD command in your Dockerfile. Notice this is the case in the below file as well.

    As an example, we’ll be using the below Dockerfile

     FROM node:18.9.0-alpine3.15
    
     WORKDIR /app
     COPY package.json ./
     RUN npm i
    
     COPY . ./
    
     # Start and enable SSH
     RUN apk add openssh \
         && echo "root:Docker!" | chpasswd \
         && chmod +x /app/init_container.sh \
         && cd /etc/ssh/ \
         && ssh-keygen -A
    
     COPY sshd_config /etc/ssh/
    
     EXPOSE 8080 2222
    
  3. In the portal under Configuration -> General Settings, set the “Startup Command” to /home/site/wwwroot/init_container.sh

    Init Script

    You can alternatively do this in the Deployment Center:

    Init Script

  4. Save the configuration. The container should now be using the .sh file being referenced for container start up.

Troubleshooting

A common issue when trying to use this option is encountering OCI runime create failed, this would look like the following - below are 2 examples:

  • Error: failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "java -jar probes-0.0.1-SNAPSHOT.jar": stat java -jar probes-0.0.1-SNAPSHOT.jar: no such file or directory: unknown
  • Error: failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "dbt build --select stage+": executable file not found in $PATH: unknown

Validate the following if OCI runtime create failed is being seen when using “startup command/file”:

  • For files being executed - make sure the file has correct line-endings - this needs to be LF - if these are CLRF then this may cause a OCI runtime create failed issue. You can validate the line-endings of a file by going into a text editor like VSCode - see Docker run fails with standard_init_linux.go error
  • For files - ensure that storage is mounted - use an FTP client or through Kudu to validate the file exists somewhere under /home
  • Use a full absolute path to the file or executable being invoked
  • Ensure the path specified exists and is correct
    • For the first two points, you may see a message like no such file or directory: unknown in the OCI error
  • If trying to invoke a command globally, make sure it’s on $PATH
    • For executables not on $PATH, you may see executable file not found in $PATH: unknown
  • Make sure the command has proper syntax and fully formed - ex. python /some/path/python.py
    • You may see no such file or directory: unknown for commands incorrectly set

For further information, see the public blog - Troubleshooting OCI runtime create errors.