Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .icons/oracle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 78 additions & 0 deletions registry/anis/templates/oci-vm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
display_name: Oracle Cloud VM (Linux)
description: Provision Oracle Cloud Infrastructure (OCI) instances as Coder workspaces
icon: ../../../../.icons/oracle.svg
verified: false
tags: [vm, linux, oracle, oci]
---

# Remote Development on Oracle Cloud Infrastructure (OCI)

Provision OCI virtual machines as [Coder workspaces](https://coder.com/docs/workspaces) using this Terraform template.

## Prerequisites

To deploy Coder workspaces on Oracle Cloud, you’ll need the following:

### OCI Resources

Before deploying, ensure your Oracle Cloud tenancy has:

- A **VCN (Virtual Cloud Network)** already created
- At least one **subnet** within that VCN (can be public or private)
- An **Internet Gateway** attached to the VCN
- A **Route Table** that routes `0.0.0.0/0` traffic to the Internet Gateway

> [!NOTE]
> This template **does not create networking resources** (VCN, subnet, gateway, etc.).
> You must reference an existing subnet using its **OCID** via the `subnet_id` variable.

> The available regions and instance shapes listed in this template are examples only, not all shapes are available in every region, and availability depends on your OCI tenancy and subscription tier. Check the [OCI documentation](https://docs.oracle.com/en-us/iaas/Content/Compute/References/computeshapes.htm) to confirm which shapes are available in your target region before deploying.

> [!IMPORTANT]
> Your tenancy is only auto-subscribed to its **home region** — everything else requires an explicit region subscription (**Governance & Administration > Region Management** in the OCI Console). `us-ashburn-1` (the default here) is one of OCI's two original commercial regions and a common home region, but it isn't universal. If your tenancy's home region is different (for example `us-phoenix-1`), change the `region` parameter's default to match — otherwise both `coder templates push` and workspace builds fail with what looks like a credentials/authentication error, even though the credentials themselves are fine.

### OCI Authentication

You’ll also need the following credentials:

- **Tenancy OCID**
- **User OCID**
- **Fingerprint**
- **Private Key**
- **Compartment OCID**(Optional) default to Tenancy OCID if not defined
- **Subnet OCID**

[OCI Documentation](https://docs.oracle.com/en-us/iaas/Content/dev/terraform/configuring.htm#api-key-auth)

---

## Workspace Lifecycle

| Event | OCI Resources |
| --------------------- | --------------------------------------------------------------------------------------- |
| **Workspace started** | A new compute instance (`oci_core_instance`) is created and the home volume is attached |
| **Workspace stopped** | The compute instance is destroyed, but the home volume (`oci_core_volume`) persists |
| **Workspace deleted** | All resources are destroyed, including the home volume |

> Only `/home/<username>` (mounted from the persistent block volume) survives a stop. Everything else, including the root disk and anything installed outside `/home`, is recreated from the base Ubuntu image on every start.

### Debugging

If you set the `ssh_public_key` template variable, its matching private key can be used to `ssh <username>@<instance-public-ip>` directly, independent of the Coder agent — useful if the agent itself fails to come up.

---

## Example `.tfvars` File

```hcl
tenancy_ocid = "ocid1.tenancy.oc1..xxxx"
user_ocid = "ocid1.user.oc1..xxxx"
fingerprint = "aa:bb:cc:dd:ee:ff"
subnet_id = "ocid1.subnet.oc1.iad.aaaaaaaaxxx"
private_key = <<EOT
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASC...
-----END PRIVATE KEY-----
EOT
```
97 changes: 97 additions & 0 deletions registry/anis/templates/oci-vm/cloud-init/cloud-config.yaml.tftpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#cloud-config
hostname: ${hostname}
users:
- name: ${username}
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
groups: sudo
shell: /bin/bash
packages:
- git
- curl
- wget
- unzip
write_files:
- path: /opt/coder/init
permissions: "0755"
encoding: b64
content: ${init_script}
- path: /opt/coder/mount-home-volume.sh
permissions: "0755"
content: |
#!/bin/bash
set -euo pipefail

label="${home_fs_label}"
mount_point="/home/${username}"
device="/dev/sdb"

mkdir -p "$mount_point"

# The block volume attaches to the instance asynchronously (a separate
# OCI API call after the instance itself is running), so it usually
# isn't present yet when cloud-init boots. Poll for it instead of
# assuming it's already there.
for i in $(seq 1 60); do
udevadm settle --timeout=2 || true
[ -b "$device" ] && break
sleep 2
done

if [ ! -b "$device" ]; then
echo "mount-home-volume: $device never showed up, giving up" >&2
exit 0
fi

# Only format on first boot. On every later stop/start cycle the
# volume already has our filesystem + label, and re-running mkfs
# would destroy whatever the user saved in their home directory.
if ! blkid "$device" >/dev/null 2>&1; then
mkfs.ext4 -L "$label" "$device"
fi

if ! grep -q "LABEL=$label" /etc/fstab; then
echo "LABEL=$label $mount_point ext4 defaults,nofail 0 2" >> /etc/fstab
fi

mount "$mount_point"
chown "${username}:${username}" "$mount_point"

# cloud-init's own ssh_authorized_keys handling runs before this
# script and writes to the root disk, but mounting the home volume
# here immediately shadows that. Provision the debug key directly on
# the mounted volume instead, so it actually survives.
ssh_public_key="${ssh_public_key}"
if [ -n "$ssh_public_key" ]; then
mkdir -p "$mount_point/.ssh"
chmod 700 "$mount_point/.ssh"
touch "$mount_point/.ssh/authorized_keys"
grep -qxF "$ssh_public_key" "$mount_point/.ssh/authorized_keys" || echo "$ssh_public_key" >> "$mount_point/.ssh/authorized_keys"
chmod 600 "$mount_point/.ssh/authorized_keys"
chown -R "${username}:${username}" "$mount_point/.ssh"
fi
- path: /etc/systemd/system/coder-agent.service
permissions: "0644"
content: |
[Unit]
Description=Coder Agent
After=network-online.target
Wants=network-online.target

[Service]
User=${username}
ExecStart=/opt/coder/init
Environment=CODER_AGENT_TOKEN=${coder_agent_token}
Restart=always
RestartSec=10
TimeoutStopSec=90
KillMode=process

OOMScoreAdjust=-1000
SyslogIdentifier=coder-agent

[Install]
WantedBy=multi-user.target
runcmd:
- /opt/coder/mount-home-volume.sh
- systemctl enable coder-agent
- systemctl start coder-agent
Loading