Customize container images

The preconfigured base images provided by Cloud Workstations contain only a minimal environment with IDE, basic Linux terminal and language tools and a sshd server. To expedite the environment setup of specific development use cases, you can create custom container images that extend these base images to pre-install tools and dependencies and that run automation scripts.

For custom container images, we recommend setting up a pipeline to automatically rebuild these images when the Cloud Workstations base image is updated, in addition to running a container scanning tool such as Artifact Analysis to inspect any additional dependencies you added. You're responsible for maintaining and updating custom packages and dependencies added to custom images.

Before you begin

  1. You need a machine with tooling for building container images such as Docker, and for pushing images to Artifact Registry using the Google Cloud CLI. You can use Cloud Workstations or Cloud Shell Editor for performing these steps, which have this tooling pre-installed.

  2. Select which base image you want to use from our list of supported base images, such as us-central1-docker.pkg.dev/cloud-workstations-images/predefined/code-oss:latest.

    Alternatively, you can use your own container image or use external container images by following the instructions to Use your own container image.

  3. Create a folder such as CUSTOM_IMAGE_FOLDER and a Dockerfile inside this folder extending the base image selected, as shown in the examples that follow.

Cloud Workstations base image structure

Cloud Workstations base images share the following defined structure:

  • The base image entrypoint file is set to /google/scripts/entrypoint.sh.
  • On startup, base images run files under /etc/workstation-startup.d/* in lexicographical order to initialize the workstation environment.

    The files and their behavior are as follows:

    • 000_configure-docker.sh: Configures and runs Docker inside the workstation.
    • 010_add-user.sh: Creates the default user in Cloud Workstations.

      Because the persistent disk is dynamically attached to the container, users must be added on workstation startup, not in the Dockerfile.

    • 020_start-sshd.sh: Starts the sshd service in the container.

    • 030_customize-environment.sh: Executes /home/user/.workstation/customize_environment as user.

    • 110_start-$IDE.sh: Starts the IDE for the image.

  • Cloud Workstations stores Docker images in the home directory at /home/.docker_data so that the images are preserved between sessions.

To add additional functionality during workstation startup, add your scripts in the /etc/workstation-startup.d/ directory:

  • Scripts in this directory run as root by default. To execute the scripts as a different user, use the runuser command.

  • Because scripts execute in lexicographical order, we recommended that you prefix the scripts with a three digit number that is greater than 200.

Alternatively, if you don't want to extend a workstations image, you can create a customize_environment script in your home directory.

Home directory modifications

When the workstation configuration specifies a persistent home directory (which is the default behavior), a persistent disk backing the home directory is dynamically attached to the container at runtime. This process overwrites modifications made to the /home directory at container image build time.

To preserve updates, modify the /home directory at container runtime by adding a script in the in the /etc/workstation-startup.d directory, or by adding per-user configuration in the /etc/profile.d directory. To speed up the process, consider running the setup script as a background process (add an ampersand, &, to the end of the command) to avoid blocking container startup.

Some examples of build time configuration that should be moved to container runtime:

  • Per-user git configuration
  • git repositories cloned in the home directory
  • Direct user configuration, such as placing files in a $HOME/.config directory
  • User creation

User creation and modification

Because the persistent disk dynamically attaches to the container at runtime, users must be added on workstation startup, not in the Dockerfile. To modify or create additional users, we recommended that you update /etc/workstation-startup.d/010_add-user.sh, or create your own script that executes on startup.

Additionally, you can modify the default bash profile for the users by updating the files in /etc/profile.d.

Update preconfigured Secure APT keys

Cloud Workstations base images come preinstalled with a number of tools obtained from various third-party repositories using Secure APT. As part of the install process, public keys provided by the repository owners are imported using gpg and placed into individual files under /usr/share/keyrings/. These files are referenced from corresponding list files under /etc/apt/sources.list.d/. This enables apt to verify the integrity of the a given repository when interacting with it.

On occasion, third-party repository owners may decide to change the public key used to validate the integrity of their repository, which causes apt to display an error when interacting with it. To resolve this potential problem, you can use /google/scripts/refresh-preinstalled-apt-keys.sh, which obtains the latest versions of preinstalled public keys and re-imports them.

You can also run this script directly in your custom Dockerfile before you run apt-get update:

# Refresh the preinstalled APT keys
RUN /google/scripts/refresh-preinstalled-apt-keys.sh

RUN apt-get update

List installed IDE versions

Several Cloud Workstations base images come preinstalled with an IDE. For convenience, see the included /google/scripts/preinstalled-ide-versions.sh script, which lists the name and version information of IDEs installed in the image.

Turn off sudo root privileges

The default workstation user has sudo root access privileges in these containers. To turn off root access to the Docker container, set the CLOUD_WORKSTATIONS_CONFIG_DISABLE_SUDO environment variable to true when creating the workstation configuration.

To set this environment variable through the Google Cloud console when creating your workstation configuration, follow these steps:

  1. When creating your workstation configuration, complete the configuration for Basic information and the Machine configuration.
  2. On the Environment customization dialog, expand the Advanced container options section and select Environment variables.
  3. Click addAdd variable.
  4. Enter CLOUD_WORKSTATIONS_CONFIG_DISABLE_SUDO and true as the value.

Customize without extending an image

For convenience all Cloud Workstations base images check for the presence of an executable file located at /home/user/.workstation/customize_environment and, if it exists, run it in the background as user. This lets you run any script or binary at startup. Unlike .profile or .bashrc the script only runs once when the workstation starts, rather than once for each shell login.

Because the customize_environment script runs as user, be sure to update permissions as necessary when writing your script. For example, if you want to install Emacs everytime your workstation starts, the content of customize_environment might be similar to the following:

#!/bin/bash
sudo apt-get update
sudo apt-get install -y emacs

Execution logs for customize_environment can be found in the container at /var/log/customize_environment and are also written to the container output logs. On successful execution of customize_environment, a file is created in /var/run/customize_environment_done. Because customize_environment runs in parallel with Workstation startup, packages installed by the script can be available as early as a few moments after your workstation has started.

Preventing idle timeouts

For convenience, all Cloud Workstations base images include a preinstalled script at /google/scripts/keep_alive.sh. This script sends regular keep-alive messages, which can prevent the workstation from shutting down due to idle timeouts when you are running background processes without direct interaction.

Use your own container image

You can also use your own container image or use external container images, as long as they are Linux-based and run a blocking process when the container starts up.

When setting up the Dockerfile, the ENTRYPOINT instruction must run a blocking process such as sleep infinity so that the container continues to run, rather than immediately exit. Alternatively, in the workstation configuration you can set the config.container.args field to specify a blocking process.

When using your own container image, note the following:

  • Cloud Workstations does not require additional scripts from the Cloud Workstations base image.

    You can, however, look at the scripts in the /etc/workstation-startup.d/ directory within a container running the Cloud Workstations base image. The filenames indicate what each script does.

  • We recommend that you run an SSH server in the container. Refer to /etc/workstation-startup.d/020_start-sshd.sh in the default base image to learn how Cloud Workstations sets this up by default.

  • We recommend that you run your default IDE or web server on port 80.

Google Cloud provides standard base images that you can use to build a fully custom container image.

Extend Cloud Workstations base images

When extending a Cloud Workstations base image to create a custom image for your workstation environment, you can take three approaches:

  1. Update your Dockerfile to include any additional static assets that you want to add.
  2. Add additional executable files under /etc/workstation-startup.d/ to customize the running container. Files under this directory automatically run in lexicographical order at container startup, so you can prefix your filename to run it at the appropriate time during workstation startup.
  3. Override the ENTRYPOINT in your Dockerfile to fully customize your container startup.

Sample custom Dockerfiles

This section provides example scenarios and instructions for creating your own Dockerfiles.

Container image with emacs pre-installed

To create a container image with emacs pre-installed, run the following commands:

FROM us-central1-docker.pkg.dev/cloud-workstations-images/predefined/code-oss:latest

RUN sudo apt update
RUN sudo apt install -y emacs

Container image with user customization

Follow these steps to customize a container image:

  1. Create a script in /etc/workstation-startup.d/* that runs after 010_add-user.sh—for example, 011_customize-user.sh:

    #!/bin/bash
    # Create new group
    groupadd $GROUP
    # Add the user to a new group
    usermod -a -G $GROUP $USERNAME
    

    Replace $GROUP with the new group name and $USERNAME with the user's username.

  2. Assuming that you named your script, 011_customize-user.sh, add the following to your image in your Dockerfile and make it executable:

    FROM us-central1-docker.pkg.dev/cloud-workstations-images/predefined/code-oss:latest
    
    COPY 011_customize-user.sh /etc/workstation-startup.d/
    
    RUN chmod +x /etc/workstation-startup.d/011_customize-user.sh
    

Container image that sets container environment variables in SSH sessions

Environment variables set at the workstation configuration or workstation level are passed to direct subprocesses using the entrypoint command. This includes the IDE in the preconfigured base images. However, SSH sessions are not child processes of the entrypoint, and don't have these custom environment variables set.

To set those environment variables in the SSH sessions, setup a custom container image that relays these environment variables from the container's entrypoint command to the /etc/environment file.

To accomplish that, follow these steps:

  1. Create a script in /etc/workstation-startup.d/* that runs after 010_add-user.sh—for example, 011_add-ssh-env-variables.sh:

    #!/bin/bash
    #
    echo "CUSTOM_ENV_VAR=$CUSTOM_ENV_VAR" >> /etc/environment
    

    Replace CUSTOM_ENV_VAR with the intended environment variable name.

  2. Assuming that you named your script, 011_add-ssh-env-variables.sh, add the following to your image in your Dockerfile and make it executable:

    FROM us-central1-docker.pkg.dev/cloud-workstations-images/predefined/code-oss:latest
    
    COPY 011_add-ssh-env-variables.sh /etc/workstation-startup.d/
    
    RUN chmod +x /etc/workstation-startup.d/011_add-ssh-env-variables.sh
    

Container image that enables X11 forwarding for SSH sessions

X11 forwarding lets you start remote applications and forward the application display to a local machine.

To create a container image that enables X11 forwarding, modify the OpenSSH daemon configuration file (/etc/ssh/sshd_config) provided by the Cloud Workstations base images by appending X11Forwarding yes (to permit X11 forwarding) and AddressFamily inet (to ensure that only IPv4 is used). For more information about these keywords, see the OpenBSD web pages about AddressFamily and X11Forwarding.

Here's a sample Dockerfile, which makes the necessary modifications: