Deploy Serverless Containers to Scaleway with OpenTofu

5 min read 760 words

After building multiarch Docker containers for Rust applications, you want to deploy them somewhere. Scaleway’s serverless container platform offers a straightforward way to run containers without managing infrastructure, and OpenTofu provides the infrastructure-as-code tooling. This guide walks through deploying Docker containers to Scaleway using the OpenTofu provider, from setting up the registry to running your container.

Prerequisites

Before we start, you’ll need:

Installing OpenTofu

OpenTofu is an open-source fork of Terraform that provides infrastructure-as-code capabilities.

# Install OpenTofu on macOS with Homebrew
$ > brew install opentofu

Let’s verify the installation worked:

$ > tofu version

OpenTofu v1.9.0
on darwin_arm64

Setting Up Scaleway Credentials

First things first, you’ll need to create API credentials in the Scaleway console. Once you’ve got them, we can store them as environment variables.

$ > export SCW_ACCESS_KEY="your-access-key"
$ > export SCW_SECRET_KEY="your-secret-key"
$ > export SCW_DEFAULT_REGION="fr-par"
$ > export SCW_DEFAULT_PROJECT_ID="your-project-id"
$ > export SCW_DEFAULT_ORGANIZATION_ID="your-organization-id"

This will use the Paris location in France for the running container.

Creating the Container Registry

Next, create an OpenTofu configuration file main.tf for a container registry to store the Docker image

terraform {
  required_providers {
    scaleway = {
      source = "scaleway/scaleway"
      version = "~> 2.64"

    }
  }
  required_version = ">= 0.13"
}

provider "scaleway" { }

resource "scaleway_container_namespace" "main" {
  name        = "rocket-example"
  description = "test container"
}

output "container_registry" {
  value       = "${scaleway_container_namespace.main.registry_endpoint}"
  description = "Registry endpoint for the container"
}

Now let’s initialise OpenTofu and plan the registry:

$ > tofu init
[]

OpenTofu has been successfully initialised!

$ > tofu plan
[]

Have a quick look at the plan to see what’ll be created, then apply it:

$ > tofu apply tfplan
scaleway_registry_namespace.main: Creating...
scaleway_registry_namespace.main: Creation complete after 2s

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

container_registry = "rg.fr-par.scw.cloud/funcscwrocketexampler5etvjil"

The container registry is needed in the next step for tagging and pushing the Docker container.

Building and Pushing Docker Images

With the registry created, we can now build and push the Docker image. For Scaleway, you’ll need to push a architecture-specific image for amd64 platform. Here’s how to do it:

# Build for ARM64 (Scaleway only supports amd64)
$ > docker build \
    --platform linux/amd64 \
    -f Dockerfile . \
    -t rg.fr-par.scw.cloud/funcscwrocketexampler5etvjil/application:latest-amd64

# Authenticate with Scaleway registry using the CLI
$ > scw registry login

The CLI generates a temporary token and configures Docker authentication automatically. Once that’s done, you can push the image:

$ > docker push rg.fr-par.scw.cloud/funcscwrocketexampler5etvjil/application:latest-amd64

Deploying the Container

Now comes deploying the container; add the container resource to the OpenTofu configuration. Here’s the updated main.tf with the container:

terraform {
  required_providers {
    scaleway = {
      source = "scaleway/scaleway"
      version = "~> 2.64"

    }
  }
  required_version = ">= 0.13"
}

provider "scaleway" { }

resource "scaleway_container_namespace" "main" {
  name        = "rocket-example"
  description = "test container"
}

output "container_registry" {
  value       = "${scaleway_container_namespace.main.registry_endpoint}"
  description = "Registry endpoint for the container"
}

resource "scaleway_container" "main" {
  name         = "application"
  namespace_id = scaleway_container_namespace.main.id
  registry_image  = "${scaleway_container_namespace.main.registry_endpoint}/application:latest-amd64"
  port         = 8080
  cpu_limit    = 256
  memory_limit = 512
  min_scale    = 1
  max_scale    = 1

  environment_variables = {
    ROCKET_LOG_LEVEL = "debug"
    ROCKET_ADDRESS   = "0.0.0.0"
    ROCKET_PORT      = "8080"
  }

  privacy = "public"
  deploy  = true
}

output "container_url" {
  value       = scaleway_container.main.domain_name
  description = "Public URL for the container"
}

Apply the configuration and watch it deploy:

$ > tofu apply
scaleway_container_namespace.main: Creating...
scaleway_container_namespace.main: Creation complete after 2s
scaleway_container.main: Creating...
scaleway_container.main: Creation complete after 15s

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

container_registry = "rg.fr-par.scw.cloud/funcscwrocketexampler5etvjil"
container_url = "rocketexampler5etvjil-application.functions.fnc.fr-par.scw.cloud"

Verifying the Deployment

Time to see if everything’s working! Let’s test the deployed container:

$ > curl https://rocketexampler5etvjil-application.functions.fnc.fr-par.scw.cloud/
Hello, World! API is running.

You can also check the container status using the Scaleway CLI:

$ > scw container container list

ID                                    NAME         NAMESPACE ID                          STATUS  MIN SCALE
0e118806-70a4-43c9-9794-7cf35fdd943d  application  e10e600c-b00c-452c-923a-1d6e1552cff5  ready   1

Cleanup

When you’re done experimenting or need to clean up, removing all resources is straightforward:

$ > tofu destroy

This removes the container and registry namespace.

That’s It! 🎉

Working with Scaleway and OpenTofu for container deployments was a nice journey; still prefer a programmatic approach like the AWS Cloud Development Kit over OpenTofu and Terraform. But it works …