OCI Container Instances is Oracle Cloud Infrastructure's serverless way to run containers. You hand it one or more images, a shape size and a subnet, and it starts them on infrastructure Oracle manages. Each container instance is isolated at the virtual-machine level, so you get VM-grade separation without patching a host operating system and without running Kubernetes. It is OCI's counterpart to services such as AWS Fargate tasks and Azure Container Instances, and it fills the gap between Functions, which run short event-driven handlers, and OKE, Oracle's managed Kubernetes.

This guide explains the resource model from first principles, the shapes and limits as documented by Oracle in October 2026, networking and identity, health checks and restart behaviour, and a full Python SDK example. It then works through a realistic deployment, lists the failure modes that catch teams out, and ends with a checklist. Shapes, regions and limits change, so confirm the numbers against the current OCI documentation and your tenancy's service limits before you size anything.

The resource model: a pod without a cluster

The unit you create is a container instance. It is closer to a Kubernetes pod than to a single container: it owns one virtual network interface card (VNIC) with one private IP address in a subnet you choose, a pool of CPU and memory defined by its shape, an ephemeral disk, and a set of volumes. Inside it run one or more containers, up to 60 per instance, which share the network namespace, so they reach each other on localhost, like containers in a pod.

What it is not matters as much. There is no scheduler that spreads replicas, no service discovery, and no rolling deployment. A container instance does not scale itself. Those jobs fall to you, to a load balancer, or to whatever automation creates and deletes instances. The spec is also effectively immutable: the update API changes only the display name and tags, so changing an image, an environment variable or a size means creating a new instance and retiring the old one. Design your deployment flow around replacement from day one.

Architecture at a glance

Clientsinternet or VCNLoad balancerbackends = private IPsContainer instance (VM-isolated, shape CI.Standard.E4.Flex)api containervcpus_limit 1.5, mem 4 GBproxy containershares localhostemptyDir volumescratch, lost on restartConfig file / FSSread-only config, shared filesOne VNIC: private IP in subnet, NSGs15 GB ephemeral storage, restart policy, health checksOCI Vaultimage pull secretContainer Registryimages pulled at startIAM dynamic groupresource principalOCI APIsObject Storage, Streaming...HTTPpullsigned calls
One container instance: VM-isolated, one VNIC with a private IP behind a load balancer, containers sharing localhost, volumes, Vault-backed image pulls and a resource principal for OCI API calls.

Shapes, sizing and billing

Container instances use flexible shapes: you choose OCPUs and memory within a shape's bounds. Oracle's documentation lists three, all with 15 GB of ephemeral storage, one VNIC, and network bandwidth of 1 Gbps per OCPU.

ShapeProcessorOCPUsMemoryMax bandwidth
CI.Standard.E4.FlexAMD x861 to 64Up to 64 GB per OCPU, 1,024 GB total40 Gbps
CI.Standard.E5.FlexAMD x86, selected regions only1 to 94Up to 1,504 GB total100 Gbps
CI.Standard.A1.FlexAmpere Arm1 to 76Up to 64 GB per OCPU, 488 GB total40 Gbps

An OCPU on x86 shapes is one physical core with two hardware threads, so a 1-OCPU E4 instance offers two logical CPUs to its containers; an A1 OCPU is one Arm core. Per-container limits are expressed in logical CPUs (vcpus_limit, fractional values allowed) and gigabytes (memory_limit_in_gbs). If you leave them unset, each container may use everything in the instance, which is fine for a single container and dangerous for several, because one runaway process starves its siblings. A1 needs Arm images: build multi-architecture images or the instance will fail to start its containers. For the shape families behind these names, see OCI compute shapes.

Billing follows the lifecycle. An instance is billed for its shape's OCPU and memory while it is ACTIVE or UPDATING, and not while it is CREATING, INACTIVE (stopped), FAILED or being deleted. Stopped and failed instances still count against your service limits, so clean them up rather than letting them accumulate. Check Oracle's price list for current per-OCPU and per-GB rates.

Networking

Each instance gets one VNIC in a subnet of your virtual cloud network. You can assign a public IPv4 address, but for anything serving production traffic the better pattern is a private subnet with network security groups (NSGs) attached to the VNIC, and an OCI load balancer in front whose backend set lists the instances' private IP addresses. Outbound access to the registry and to OCI APIs then goes through a NAT gateway or a service gateway. If the VCN and subnet have DNS labels and you ask for a private DNS record, the instance also gets a hostname inside the VCN. The general rules for routing, gateways and security lists are covered in OCI networking.

Because all containers in an instance share one IP and one network namespace, two containers cannot listen on the same port. Treat ports like a pod's: the application on 8080, the proxy on 443, the metrics sidecar on 9090.

Identity: who creates instances and what they can do

Two identity questions arise: who may create instances, and what may the instances themselves do. People and pipelines need manage compute-container-family in the instance's compartment, use access to the network resources, and read access to repositories if they pick images from Container Registry. The instances need their own permission to pull private images, which you grant to a dynamic group that matches the computecontainerinstance resource type. The policy statements below follow Oracle's documented examples; replace the names with your own.

# Dynamic group matching container instances in one compartment
ALL {resource.type='computecontainerinstance', resource.compartment.id = '<ci-compartment-ocid>'}

# Let them pull images from Container Registry
Allow dynamic-group OrdersContainerInstances to read repos in compartment registry

# Let the people (or pipeline) who deploy them create and manage them
Allow group OrdersDeployers to manage compute-container-family in compartment orders
Allow group OrdersDeployers to use virtual-network-family in compartment network
Allow group OrdersDeployers to read repos in tenancy

For registries outside OCI, or when you prefer not to grant the instance registry access, supply an image pull secret. The secret can be basic credentials inline, which ends up in the instance definition, or a reference to a secret in OCI Vault, which is the option to choose. Inside the containers, application code can call other OCI services with the instance's resource principal; each container has a flag, is_resource_principal_disabled, that turns this off, and you should set it for containers that do not need OCI API access. Then write narrow policies for the dynamic group, as described in OCI IAM, and keep application secrets in OCI Vault rather than in environment variables.

Restart policy, health checks and shutdown

Three settings decide how the instance reacts when a process misbehaves. The restart policy applies to every container in the instance: ALWAYS (the default) restarts a container whenever it exits, even with code zero, which suits servers; ON_FAILURE restarts only on a non-zero exit, which suits jobs that should run to completion; NEVER leaves exited containers stopped.

Health checks are per container and come in two types, HTTP (a path and port) and TCP (a port). Each has an initial delay, an interval, a timeout, failure and success thresholds, and a failure action. The action KILL is the default: the container is stopped and the restart policy decides what happens next. NONE only reports the status, which is useful while you calibrate thresholds. Set the initial delay longer than your slowest cold start, or a healthy but slow container will be killed in a loop.

The graceful shutdown timeout is how long processes get to finish after a stop is requested before they are terminated. Oracle's console documentation describes a default of zero seconds, so set it explicitly, and make sure your process handles SIGTERM by draining connections and flushing buffers within that window.

Security context

Each container can carry a security context. Use it. Set a non-zero user and group ID and enable run-as-non-root, which rejects user ID 0. Enable a read-only root filesystem and give the process an emptyDir volume for scratch space. Drop Linux capabilities: by default a set similar to Docker's is enabled, including CAP_NET_RAW, CAP_SETUID and CAP_CHOWN; dropping ALL and adding back only what the process needs, typically nothing or CAP_NET_BIND_SERVICE, sharply limits what an attacker can do after a code-execution bug.

Creating an instance with the Python SDK

The example creates one instance with two containers using the OCI Python SDK: the application and a small proxy, a shared emptyDir scratch volume, per-container CPU and memory limits, an HTTP health check, a Vault-backed pull secret, and a private VNIC with an NSG. The upper-case names are placeholders for your own OCIDs and availability domain. Class and field names follow the SDK's oci.container_instances module.

import oci
from oci.container_instances import ContainerInstanceClient
from oci.container_instances.models import (
    CreateContainerInstanceDetails, CreateContainerInstanceShapeConfigDetails,
    CreateContainerDetails, CreateContainerResourceConfigDetails,
    CreateContainerVnicDetails, CreateContainerHttpHealthCheckDetails,
    CreateVaultImagePullSecretDetails, CreateContainerEmptyDirVolumeDetails,
    CreateVolumeMountDetails,
)

config = oci.config.from_file()            # or a resource/instance principal signer
client = ContainerInstanceClient(config)

api = CreateContainerDetails(
    display_name="api",
    image_url="fra.ocir.io/mytenancy/orders-api:1.8.3",   # pin a tag, better a digest
    environment_variables={"PORT": "8080", "LOG_FORMAT": "json"},
    resource_config=CreateContainerResourceConfigDetails(
        vcpus_limit=1.5, memory_limit_in_gbs=4.0),
    health_checks=[CreateContainerHttpHealthCheckDetails(
        health_check_type="HTTP", path="/healthz", port=8080,
        initial_delay_in_seconds=10, interval_in_seconds=10,
        failure_threshold=3, timeout_in_seconds=3, failure_action="KILL")],
    volume_mounts=[CreateVolumeMountDetails(mount_path="/scratch", volume_name="scratch")],
)

proxy = CreateContainerDetails(
    display_name="proxy",
    image_url="fra.ocir.io/mytenancy/edge-proxy:2.4.0",
    resource_config=CreateContainerResourceConfigDetails(
        vcpus_limit=0.5, memory_limit_in_gbs=1.0),
)

details = CreateContainerInstanceDetails(
    compartment_id=COMPARTMENT_OCID,
    availability_domain=AD_NAME,
    display_name="orders-api-blue",
    shape="CI.Standard.E4.Flex",
    shape_config=CreateContainerInstanceShapeConfigDetails(ocpus=1, memory_in_gbs=8),
    vnics=[CreateContainerVnicDetails(subnet_id=PRIVATE_SUBNET_OCID,
                                      is_public_ip_assigned=False,
                                      nsg_ids=[API_NSG_OCID])],
    containers=[api, proxy],
    volumes=[CreateContainerEmptyDirVolumeDetails(name="scratch", volume_type="EMPTYDIR")],
    container_restart_policy="ALWAYS",
    graceful_shutdown_timeout_in_seconds=30,
    image_pull_secrets=[CreateVaultImagePullSecretDetails(
        registry_endpoint="fra.ocir.io", secret_type="VAULT", secret_id=PULL_SECRET_OCID)],
    freeform_tags={"service": "orders", "colour": "blue"},
)

resp = client.create_container_instance(details)
ci = oci.wait_until(client, client.get_container_instance(resp.data.id),
                    "lifecycle_state", "ACTIVE", max_wait_seconds=600).data
print(ci.id, ci.lifecycle_state)

Note the image reference. A mutable tag such as latest means a restarted container can come back running different code from its sibling instances, because images are pulled when containers start. Pin version tags at minimum, or digests if your registry workflow supports them. Note also that emptyDir volumes can be backed by ephemeral storage or by memory; a memory-backed volume counts against the instance's memory.

Worked example: an internal API and a nightly job

A team runs an internal orders API that peaks during business hours and needs no orchestration features beyond a load balancer. Their deployment looks like this:

  1. Two container instances, one per fault domain or availability domain, each E4.Flex with 1 OCPU and 8 GB, in a private subnet behind an OCI load balancer whose health check hits /healthz.
  2. To deploy version 1.8.4, the pipeline creates two new instances tagged green, waits for ACTIVE and for the containers' own health checks to pass, adds the new private IPs to the load balancer's backend set, drains and removes the blue backends, then deletes the blue instances. Rollback is the same steps in reverse while blue still exists.
  3. A nightly reconciliation job runs as a separate instance with restart policy ON_FAILURE. It reads input from Object Storage with the resource principal, writes results back, and exits zero; the pipeline then deletes the instance, which also stops billing for it.
  4. Logs go to stdout as JSON. The pipeline pulls them with the retrieve-logs operation when a deployment fails, and the application ships them to OCI Logging for long-term search. Alarms watch the load balancer's backend health and the error rate.

Scaling is a pipeline decision: when the load balancer's request rate stays above a threshold, a scheduled job creates a third instance. If the team later needs autoscaling, service discovery and rolling updates driven by a control plane, that is the signal to move to OKE rather than to rebuild Kubernetes in shell scripts.

Failure modes

  • Expecting in-place updates. Only name and tags change in place. Every real change is create, verify, switch and delete.
  • Image pull failures. Missing dynamic-group policy, a private subnet with no route to the registry, an expired Vault secret, or an Arm-only or x86-only image on the wrong shape. Check the work request errors first.
  • Health-check kill loops. Initial delay shorter than start-up time, or a check that depends on a downstream service, kills healthy containers repeatedly.
  • Noisy neighbours inside an instance. Without per-container limits, one container can consume the whole instance's CPU or memory.
  • Data loss. Ephemeral storage and emptyDir volumes do not survive instance deletion. Persist to Object Storage, a database, or a File Storage volume.
  • Limit exhaustion. Stopped and failed instances still count against limits, so a failed deployment loop can block the next deploy.

Trade-offs against Functions, OKE and VMs

OptionChoose it whenWatch out for
Container InstancesA few long-running services or batch jobs, no cluster to runNo autoscaling, replace-to-update
OCI FunctionsShort event-driven handlers that scale to zeroExecution time limits, cold starts
OKEMany services, autoscaling, rolling deploys, service meshCluster operations and Kubernetes skills
Compute VM with DockerFull host control, special kernels or agentsYou patch and manage the OS

Container Instances is the right default when the question is 'how do I run this image reliably' rather than 'how do I run a platform'. Compare its event-driven sibling in OCI Functions before choosing.

What to do next

  1. Confirm current shapes, regional availability and your tenancy's service limits for Container Instances.
  2. Create the dynamic group and policies, and test an image pull from a private subnet.
  3. Build multi-architecture images if you may use A1, and pin tags or digests.
  4. Set per-container CPU and memory limits, HTTP or TCP health checks with a realistic initial delay, and an explicit graceful shutdown timeout.
  5. Apply a security context: non-root user, read-only root filesystem, capabilities dropped.
  6. Store pull credentials and application secrets in Vault, and disable the resource principal for containers that do not need it.
  7. Script blue-green replacement behind a load balancer, and clean up stopped and failed instances.
  8. Ship logs to OCI Logging and alarm on backend health; revisit OKE if you start building a scheduler.
Key takeaway: OCI Container Instances runs one or more containers in a VM-isolated, pod-like unit with one VNIC, a flexible E4, E5 or A1 shape and 15 GB of ephemeral storage, billed only while active. It has no scheduler, no autoscaling and no in-place spec updates, so deploy by replacement behind a load balancer. Grant image pulls through a dynamic group or a Vault-backed secret, set per-container limits, health checks, an explicit shutdown timeout and a strict security context, and move to OKE when you need a real control plane.