Creator of this small website
Jun 1, 2020 3 min read

Using Containers Directly on GCE

thumbnail for this post


GCP, and in particular its components GCE has a nice feature to run containers directly on a VM, without the need to spin up a GKE cluster or to provision a machine by yourself. It leverages the use of Container Optimized OS maintained by google.

I’m a big fan of Terraform (and most Hashicorp products) but sadly it does not support the aforementioned feature out-of-the-box. Let’s see how to make this happen easily in a simple manner

How does this work ?

A container-only instance is a regular instance, and the specifics lay down in the instance metadata. Let’s take a look of what it looks like.

First, let’s launch a instance :

gcloud compute instances create-with-container test-vm --container-image --zone=zzz --network=yyy --subnet=xxx --container-env="foo=bar,spam=baz"

Then let’s look into its metadata :

gcloud compute instances describe test-vm --format=json | jq

And in the output

  "metadata": {
    "items": [
        "key": "google-logging-enabled",
        "value": "true"
        "key": "gce-container-declaration",
        "value": "spec:\n  containers:\n    - name: test-vm\n      image: ''\n      env:\n        - name: foo\n          value: bar\n        - name: spam\n          value: baz\n      stdin: false\n      tty: false\n  restartPolicy: Always\n\n# This container declaration format is not public API and may change without notice. Please\n# use gcloud command-line tool or Google Cloud Console to run Containers on Google Compute Engine."
    "kind": "compute#metadata"

Note the warning embedded, but also that the metadata is simply YAML

The terraform version

After seeing this, all we need is to reproduce the work done by the gcloud command.

# this is maintained by GCP -> Container Optimized OS
data "google_compute_image" "cos" {
  family = "cos-stable"
  project = "cos-cloud"

data "google_compute_default_service_account" "default" {}

resource "google_compute_instance" "container_vm" {
  name = "container_vm"
  machine_type = "f1-micro"
  allow_stopping_for_update = true

  network_interface {
# snip

  boot_disk {
    initialize_params {
      image = data.google_compute_image.cos.self_link

  service_account {
    email =
    scopes = [

  metadata = {
    # This container declaration format is not a public API and may change without notice
    # Use gcloud command-line tool or Google Cloud Console to create a new one and dump metadata if it breaks
    gce-container-declaration =<<EOT
    - image: your_image_here:latest
      name: containervm
        privileged: false
        - name: foo
          value: bar
        - name: meh
          value: spam
      stdin: false
      tty: false
      volumeMounts: []
      restartPolicy: Always
      volumes: []
    google-logging-enabled = "true"

And that’s it ! You now have a hassle free dedicated image running.

If you get your metadata wrong then the instance will be created but won’t launch the container; and the gce-container-declaration field will appear in the metadata.