Migrate containers that were deployed on VMs during VM creation Stay organized with collections Save and categorize content based on your preferences.
Thecontainer startup agent in Compute Engine is deprecated. This agent lets youdeploy containers on Compute Engine instances when you create VMs.
This document describes how to migrate existing containers that the startupagent created on your VMs or managed instance groups (MIGs) to otherGoogle Cloud services.
Based on your requirements, choose one of the following options tomigrate the containers that were deployed on VMs using the deprecated method:
- If you want to continue running containers on individual VMs and MIGs, usestartup scripts orcloud-init.
- If you have stateless container applications and small to medium jobs,useCloud Run.
- If your container is a batch job that has a definite end state and requiresadditional computing resources, useBatch.
- If you need advanced control and scalability or if you can't meet yourrequirements with the other options, useGKE on Google Cloud.
For more use cases and alternative solutions, seeCompare the container deployment options.
Deprecated options for configuring containers on VMs
When you configure a container during VM creation,Compute Engine uses the container startup agent to read thegce-container-declaration metadata that stores thecontainer information, and to deploy the container on the VM.
The following options fordeploying containers directly on a VM or MIGthat use the container startup agent and thegce-container-metadata are deprecated.
Console
TheDeploy container option on theCreate an instance page is deprecated:

gcloud
The followinggcloud commands that configure a container on a VM or an instance template are deprecated:
- gcloud compute instances create-with-container
- gcloud compute instances update-container
- gcloud compute instance-templates create-with-container
- gcloud compute instances create command that uses the
--metadataflag to set thegce-container-declarationmetadata key - gcloud compute instance-templates create command that uses the
--metadataflag to set thegce-container-declarationmetadata key
Terraform
The Terraform modulegce-containerand thegce-container-declaration metadata key to configure containers are deprecated.
Identify instances that use the deprecated container metadata
To identify instances in your project that use the deprecated containermetadata, follow these steps to check for thegce-container-declarationmetadata key:
Run one of the following commands:
To list all instances in your project that use the
gce-container-declarationmetadata key and value, run the followingGoogle Cloud CLI command:gcloudcomputeinstanceslist--filter="metadata.items.key:gce-container-declaration"This command provides a list of all VM instances in your configuredproject that contain the
gce-container-declarationmetadata key.The metadata key uniquely identifies VMs that are in scope of thedeprecation. If you are using multiple projects, run this commandacross all of the active projects.To validate a specific instance, run the following Google Cloud CLI command:
gcloudcomputeinstancesdescribeVM_NAME--format="(metadata.items)"Replace
VM_NAMEwith the name of the VMinstance that you want to validate.
If the output of the command from the preceding step listsinstances that use the
gce-container-declarationmetadata key, runthe following command to get more details about the containerdeclaration on your VMs:gcloudcomputeinstanceslist\--filter='metadata.items.key:gce-container-declaration AND (metadata.items.value~"image:")'\--format="table(name, zone, metadata.items.filter(key='gce-container-declaration').extract(value).slice(0):label=CONTAINER_DECLARATION)"Based on the output of the command, consider the following:
If the metadata contains the definition for the deprecated container startup agent, thenyou must migrate the container deployment to an alternative solution as described inthis document.
If the
gce-container-declarationmetadata key is present, but you arenot using it for the container startup agent, take the following actions:- Check if you are reusing this metadata key for other configurations.
- If you are reusing the key, use a different metadata key for other configurations.
For more information about viewing metadata, seeView and query metadata.
Compare the container deployment options
The following table summarizes the use cases for running containers on VMs andrecommends alternative container solutions for migrating your workloads:
| Use cases | Replacement type | Cost | Recommended solution |
|---|---|---|---|
| Direct replacement | No additional cost | Use startup scripts to deploy containers on VMs. | |
For example, create users,import files, mount disks, or use privileged mode. | Direct replacement | No additional cost | Usecloud-init to run tasks during the VM lifecycle. |
| Run a batch job that has a definite end state and requires additional computing resources. | Managed service | Depends on your workload characteristics and complexity of the container configuration. | Batch |
| Managed service | No to low cost solution for smaller workloads. | Cloud Run | |
| Managed service | Depends on workload characteristics and complexity of the container configuration. | Google Kubernetes Engine |
When you transition from the Compute Engine container startup agent to analternative solution, consider the following required changes and the potential effort of implementing them:
- VMs running Container-Optimized OS: Take full ownership of VM and containerruntime setup, configuration, security, and maintenance, which ofteninvolves scripting with startup scripts or
cloud-init. - Cloud Run or Batch: Ensure that your applications arestateless and fit the request-driven or job-based execution model. Thisapproach might involve adaptingapplications to work with external state management services.
- GKE: Adopt Kubernetes principles, define workloads by usingKubernetes manifest files, and manage cluster resources.
Use startup scripts to deploy containers on VMs
You can run a basic container on a VM using a startup script.
Consider the following points when you use a startup script to configure containers:
- You can use a startup script for basic scenarios. For advanced configuration,considerusing
cloud-init. - Because you are creating a new VM with a container configured using the startup script, you must planfor the transition of any workloads deployed on the existing VMs.
- Test and ensure that everything works as expected before you route trafficto your newly created VM with a container.
To create a VM and to deploy a container on a VM or a MIG, do the following:
- Map current container on VM metadata to startup script command
- Create a startup script based on the existing metadata configuration
- Create a VM by using the startup script orCreate a MIG by using the startup script.
Map your container metadata todocker run command
You can map the VM metadata orgcloud flagstodocker run arguments and include this in your startup script for creating VMs.
Somegcloud flags translate directly to VM metadata. These flags alsotranslate directly todocker run flags.If you have an existing container on a VM, you can read the VM metadataconfiguration and build a startup script using the equivalentdocker run commands.
# Get your existing VM instance configuration in yaml formatgcloudcomputeinstancesdescribeVM_NAME--format="(metadata.items)"The output is similar to the following:
metadata:items:-key:gce-container-declarationvalue:|spec:containers:-args:-'"hello world!"'command:-echoenv:-name:ONEvalue:'1'image:docker.io/library/busyboxname:my-instancesecurityContext:privileged:truestdin:truetty:truerestartPolicy:Always-key:google-logging-enabledvalue:'true'Use the following table to map existing specification todocker run commands:
| Google Cloud CLI flag | VM metadata key | Docker run command |
|---|---|---|
--container-image | containers.image | Specify as an argument without any flag. For example: docker run gcr.io/google-containers/busybox |
--container-command | command | Specify as an argument without any flag, after the container image name. For example: docker run gcr.io/google-containers/busybox echo "hello world" |
--container-arg | args | Specify as an argument without any flag, after the command. For example: docker run gcr.io/google-containers/busybox echo "hello world" |
--container-env | containers.env array | --env KEY=VALUE [--env KEY=VALUE ...] |
--container-restart-policy | restartPolicy | --restartPossible values are no,on-failure, andalways. Default isno. |
--container-stdin | containers.stdin | -iBoolean flag, true if present, false by default. |
--container-tty | containers.tty | -tBoolean flag, true if present, false by default. |
--container-privileged | containers.securityContext.privileged | --privilegedBoolean flag, true if present, false by default. |
--container-mount-disk | - | No equivalentdocker run command.You canmount the disk separately. |
Example startup scripts
The following examples show how to include thedocker commands in your startupscript:
- Example 1: runs a standalone container in a VM based onContainer-Optimized OS.
Example 2: runs a web server container in a VM based onContainer-Optimized OS.
Example 1
Run a standalone container in a VM based on Container-Optimized OS:
#!/bin/bash# A name for the containerCONTAINER_NAME="my-app-container"# Stop and remove the container if it existsdockerstop$CONTAINER_NAME||truedockerrm$CONTAINER_NAME||true# Pull the latest version of the container image from Docker Hubdockerpullbusybox:latest# Run docker container from image in docker hubdockerrunbusybox:latest\echo"hello world!"Example 2
Run a web server container in a VM based on Container-Optimized OS:
#!/bin/bash# Enable incoming trafficiptables-AINPUT-jACCEPT# A name for the containerCONTAINER_NAME="my-app-container"# Stop and remove the container if it existsdockerstop$CONTAINER_NAME||truedockerrm$CONTAINER_NAME||true# Pull the latest version of the container image from Docker Hubdockerpullnginx:latest# Run docker container from image in docker hubdockerrun\--name=$CONTAINER_NAME\--privileged\--restart=always\--tty\--detach\--network="host"\nginx:latestAdditional configuration options for container deployment
This section describes the additional configuration parametersfor deploying containers on your VMs.
For more information about these options, seeConfigure options to run a container.
Access to Artifact Registry images
If you need access to container images fromgcr.io or pkg.dev,use thedocker-credential-gcr tool, which is preinstalled in Container-Optimized OS,and configure authentication to Artifact Registry for Docker.Run the following command before you run the container:
# Set home directory to save docker credentialsexportHOME=/home/appuser# Configure docker with credentials for gcr.io and pkg.devdocker-credential-gcrconfigure-dockerFor more information, seeConfigure authentication to Artifact Registry for Docker.
Configure logging
We recommend using Cloud Loggingby enabling a logging agent on a VM.
Alternatively, if you want to change the logging driver, you can includethe--log-driver parameter with yourdocker run command:
# Use Cloud Logging logging driverdockerrun--log-driver=gcplogsnginx:latestFor more information, seeUsing Cloud Logging with Container-Optimized OS
Configure internal firewall
Container-Optimized OS denies incoming traffic by default, so you must addiptables rules to allow that traffic. Note that these commands configure thehost operating system's internal firewall. Additionally, you must configure yourVirtual Private Cloud firewall to allow that traffic to the new VM
For more information, seeUse VPC firewall rules.
# Enable all incoming and routed trafficiptables-AINPUT-jACCEPTiptables-AFORWARD-jACCEPTFor more information, seeConfiguring the host firewall.
Attach volumes to the container
If volumes are attached to the container, the container metadata includes thevolumes entry and avolumeMounts array. Thename of an entry involumescorresponds to the name of an entry involumeMounts, and the other way around.For each volume that you collect, gather the required information either fromthevolumes or from thevolumeMounts entry.
If no volumes are attached to the container, you can skip this section anddirectlycreate a VM by using the startup script.
For more information about disks and file system on Container-Optimized OS,seeDisks and file system overview.
Mount tmpfs file system
To mount an empty tmpfs file system to a container, specify the--tmpfsargument with yourdocker run command. For example, to mount a cache file systemto your nginx container, run the following command:
# mount a cache file system to the nginx containerdockerrun-d--name=$CONTAINER_NAME--tmpfs/var/cache/nginx:rw,size=512m,noexec,nosuid,nodev--network="host"nginx:latestFor more information about mountingtmpfs file systems, seetmpfs mounts.
Mount a host directory
To mount a directory from a host VM to a container, specify the--mount argumentwith thedocker run command:
# mount a read-only directory to the nginx containerdockerrun-d--name=$CONTAINER_NAME--mounttype=bind,source=/var/www/html,target=/usr/share/nginx/html,ronginx:latestFor more information, seeBind mounts.
Mount a persistent disk to the container
Mounting a disk to the container requires additional steps. To mount a disk,first mount it on the VM, and then mount that disk to the container:
To mount the disk to the VM, run the following command:
#!/bin/bashDISK_DEVICE_NAME="my-persistent-disk"# This name MUST match the 'device-name' in the gcloud --disk flagDISK_BY_ID_PATH="/dev/disk/by-id/google-${DISK_DEVICE_NAME}"HOST_MOUNT_POINT="/mnt/disks/my-persistent-disk"# This is the path where the disk will be mounted on the VMCONTAINER_MOUNT_PATH="/usr/share/my-persistent-disk"# This is the path where the disk will be mounted in the container# format a disk as an ext4 filesystem, if it doesn't already contain onefile-sL$DISK_BY_ID_PATH|grep-qfilesystem||\mkfs.ext4-m0-Elazy_itable_init=0,lazy_journal_init=0,discard$DISK_BY_ID_PATH# create a directory for mounting pointsudomkdir-p"${HOST_MOUNT_POINT}"# mount a disk to the VMsudomount-odefaults,discard"${DISK_BY_ID_PATH}""${HOST_MOUNT_POINT}"After you mount the disk to the VM, add the
--mountflag with thedocker runcommand to mount the disk to the container:dockerrun-d--name=$CONTAINER_NAME--mounttype=bind,source="${HOST_MOUNT_POINT}",target="${CONTAINER_MOUNT_PATH}",readonlynginx:latest
Create a VM by using the startup script
After creating a startup script with your container configuration, use thisstartup script to create a VM based on Container-Optimized OS. For moreinformation about creating a VM based on Container-Optimized OS, seeCreate an instance from a public image.
For more information about using startup scripts, seeUsing startup scripts on Linux VMs.
Console
In the Google Cloud console, go to theCreate an instance page.
If prompted, select your project and clickContinue. TheCreate an instance page appears and displays theMachine configuration pane.
In theMachine configuration pane, select the machine familyand machine type for your VM.
In the navigation menu, clickOS and storage. In theOperating system and storage pane that appears, configure your bootdisk by doing the following:
- ClickChange. TheBoot disk pane appears and displays thePublic images tab.
- In theOperating system list, selectContainer Optimized OS.
- In theVersion list, select the OS version.
- In theBoot disk type list, select the type of the boot disk.
- (Optional) If you need additional disks, add disks in theAdditional disks section.
- ClickSelect.
In the navigation menu, clickAdvanced.
- In theAutomation section, paste the startup script that you createdfor your container deployment.
To create and start the VM, clickCreate.
gcloud
When using gcloud CLI, store a startup script in a separate file.
To create a VM by using a startup script, run the followingcommand:
gcloud compute instances createVM_NAME \ --zone=ZONE \ --image-family=IMAGE_FAMILY \ --image-project=IMAGE_PROJECT \ --machine-type=MACHINE_TYPE \ --metadata-from-file=startup-script=STARTUP_SCRIPT_FILE
Replace the following:
VM_NAME:name of thenew VM.ZONE: zone to create the instance in.IMAGE_PROJECT: the Container-Optimized OSimage project that containsthe image, for example,cos-cloud.IMAGE_FAMILY: the Container-Optimized OSimage family, for example,cos-stable.MACHINE_TYPE: machine type for the new VM, whichcan be apredefined machine typeor acustommachine type.STARTUP_SCRIPT_FILE: the relative path on your machineto thestartup script file, for example,./startup_script.sh.
Example:
# Create COS-based VM by using a startup scriptgcloud compute instances create "cos-instance-with-startup-script" \--zone="us-central1-c" \--machine-type="e2-medium" \--image-family="cos-stable" \--image-project="cos-cloud" \--metadata-from-file=startup-script="./startup_script.sh"
Verify that Compute Engine created the VM by running the following command:
gcloud compute instances describeVM_NAME
Replace
VM_NAMEwith the name of the VM you created.
Terraform
To create a VM, you can use thegoogle_compute_instanceresource.
provider "google" {project = "PROJECT_ID"}resource "google_compute_instance" "cos_vm_instance" {name = "VM_NAME"machine_type = "MACHINE_TYPE"zone = "ZONE"# Use a Container-Optimized OS image for the boot diskboot_disk { initialize_params { image = "IMAGE_PROJECT/IMAGE_FAMILY" }}# Attaches the instance to the default networknetwork_interface { network = "default"}# Specify the relative path to the startup script on your local machinemetadata = { startup-script = file("STARTUP_SCRIPT_FILE")}}Replace the following:
VM_NAME:name of the new VMZONE: zone to create the instance in.IMAGE_PROJECT: the Container-Optimized OSimage project that contains the image, for example,cos-cloud.IMAGE_FAMILY: the Container-Optimized OS image family, for example,cos-stable.MACHINE_TYPE: machine type for the new VM, which can be apredefined machine type or acustom machine type.STARTUP_SCRIPT_FILE: the relative path on your machine to thestartup script file, for example,./startup_script.sh.
Example:
provider "google" { project = "my-project"}resource "google_compute_instance" "my_container_vm" { name = "my-container-vm-startup" machine_type = "e2-medium" zone = "us-central1-a" boot_disk { initialize_params { image = "cos-cloud/cos-stable" } } network_interface { network = "default" } metadata = { startup-script = file("./startup_script.sh") }}Create a MIG by using the startup script
After creating an instance template using the startup script, use one of thefollowing methods to create a MIG.
For more information about creating MIGs, seeCreate a managed instance group.
Console
Create an instance template that isbased on the startup script you created in the previous section.
- In theOperating system section, select aContainer Optimized OSand version.
- In theAutomation section, paste the startup script that you createdfor the container deployment.
Create a MIGby using the instance template created in the previous step.
gcloud
Create an instance templateby using the
instance-templates createcommand.You must use a Container-Optimized OS image for the VM.You can specify the relative path to the startup script file in the
--metadata-from-fileflag.Create a MIGby using the instance template created in the previous step.
Example:
# Create the instance template that uses a startup script gcloud compute instance-templates create startup-template \ --machine-type=e2-medium \ --image-family=cos-stable \ --image-project=cos-cloud \ --metadata-from-file=startup-script=./startup_script.sh # Create the managed instance group gcloud compute instance-groups managed create startup-mig \ --template=startup-template \ --size=2 \ --zone=us-central1-a
Terraform
Use thegoogle_compute_instance_template andgoogle_compute_instance_group_manager resources tocreate an instance template and a MIG, as shown in the following example:
Example:
resource "google_compute_instance_template" "startup_template" { name_prefix = "startup-template-" machine_type = "e2-medium" disk { source_image = "cos-cloud/cos-stable" auto_delete = true boot = true } network_interface { network = "default" } metadata = { startup-script = file("./startup_script.sh")}}resource "google_compute_instance_group_manager" "startup_mig" { name = "startup-mig" base_instance_name = "startup-vm" zone = "us-central1-a" version { instance_template = google_compute_instance_template.startup_template.id } target_size = 2}Test and clean up
After successful creation of a VM or a MIG, validate that your applicationis running on the container and working as expected. To fix any issues, seeTroubleshooting.
If the application is running successfully on your new VMs created using thestartup script, you can delete theVMsandMIGs that use the deprecated method of deploying containers.
Troubleshooting startup script issues
This section provides troubleshooting information for issues that you mightencounter when using startup scripts.
Unable to save docker configuration
When you run thedocker-credential-gcr configure-dockercommand in a startup script,you might get the following error message:
ERROR: Unable to save docker config: mkdir /root/.docker: read-only file system
This error occurs becausedocker-credential-gcr attempts to write credentialsto the/root/.docker/config.json file. Theroot file system onContainer-Optimized OS is read-only, so you can't write to it.
To resolve this issue, set the environment variable$HOME topoint to a custom home directory before you run thedocker commands.
Example:
export HOME=/home/appuser docker-credential-gcr configure-docker
View logs to debug issues
To troubleshoot issues that might occur when you configure containers on VMs usinga startup script, view the startup script logs and the container logs.
To view startup script logs on the VM instance, run the following command:
sudo journalctl | grep "startup-script"
To view logs from the Docker container, run the
docker logscommand:docker logsCONTAINER_NAME
Replace
CONTAINER_NAMEwith the name of your container.
For troubleshooting other issues, see the following documents:
- Cloud Logging overview
- Using Cloud Logging with Container-Optimized OS
- Troubleshooting the Docker daemon
- Troubleshoot and diagnose
- Troubleshoot Terraform
- Troubleshooting when running a basic web server
- Set up and manage network address translation with Public NAT
Usecloud-init with Container-Optimized OS
You can usecloud-init,an industry-standard and cross-platform solution, to deploy containers on VMsrunning Container-Optimized OS.This tool lets you run custom configuration during the VM creation or startup.For more information, seeUsingcloud-init with the Cloud config format.
Use managed services for container deployment
This section describes the managed services provided by Google Cloud that you canuse to deploy containers.
Cloud Run
Cloud Run is a good option for stateless container applications and small to medium jobs.
Key features of Cloud Run include the following:
- You can choose to only allocate CPUs during request processing, or always allocate CPUs.
- You can run a stateless container application or execute a job as one-off, on a schedule, or as part of a workflow.
- You can configure timeouts for each request or task.
- It's highly scalable and secure.
- It has integrated load balancing and autoscaling.
For more information about deploying containers on Cloud Run, seeDeploying container images to Cloud Run
Batch
Batch is a fully managed service that lets you schedule, queue,and execute batch processing workloads on Google Cloud resources. It's designedfor running batch-style, parallelizable workloads, including those packaged in containers.
For more information about deploying containers on Batch, see the following documents:
Google Kubernetes Engine
If you are running complex applications, microservices, continuous operationand need fine-grained control and scalability, Google Kubernetes Engine (GKE) is the offeringthat is best suited. For more information about deploying containers on GKE.see the following documents:
Get support
If you have any questions about the migration process or if you need assistance,review theFAQor contactGoogle Cloud Support.
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2026-02-19 UTC.