oci-free-dev-env
Create a free remote development host on Oracle Cloud (Always Free) end to end — install and authenticate the OCI CLI, provision VCN/subnet/NSG/instance with capacity retries, then apply a hardened Ubuntu 22.04 Ansible stack (Tailscale-only SSH, ufw, fail2ban, mosh, persistent tmux, Caddy tailnet web previews, on-host verification suite) and tear it down again. Use when the user says "free remote dev environment", "Oracle Cloud free tier dev box", "OCI Always Free VPS", "give me a cloud dev machine for free", "provision a dev host on Oracle", or asks to move a Tailscale-only dev host setup to Oracle Cloud. Covers OCI CLI bootstrap, the Tailscale cutover, verification, costs, and teardown.
含まれるファイル(48)
- SKILL.md9.7 KB
- .env.example3.1 KB
- .gitignore187 B
- ansible/ansible.cfg535 B
- ansible/files/ssh_config1.1 KB
- ansible/group_vars/all/main.yml3.9 KB
- ansible/group_vars/all/secrets.yml.example580 B
- ansible/inventory.ini.example565 B
- ansible/roles/caddy/defaults/main.yml325 B
- ansible/roles/caddy/handlers/main.yml171 B
- ansible/roles/caddy/tasks/main.yml3.5 KB
- ansible/roles/caddy/templates/Caddyfile.j21.4 KB
- ansible/roles/common/defaults/main.yml593 B
- ansible/roles/common/handlers/main.yml112 B
- ansible/roles/common/tasks/main.yml3.3 KB
- ansible/roles/fail2ban/defaults/main.yml480 B
- ansible/roles/fail2ban/handlers/main.yml96 B
- ansible/roles/fail2ban/tasks/main.yml958 B
- ansible/roles/firewall/tasks/main.yml2.2 KB
- ansible/roles/mosh/tasks/main.yml1.5 KB
- ansible/roles/sshd/handlers/main.yml87 B
- ansible/roles/sshd/tasks/main.yml2.0 KB
- ansible/roles/sshd/templates/99-devhost-hardening.conf.j2466 B
- ansible/roles/storage/tasks/main.yml2.9 KB
- ansible/roles/tailscale/defaults/main.yml420 B
- ansible/roles/tailscale/handlers/main.yml66 B
- ansible/roles/tailscale/tasks/main.yml6.1 KB
- ansible/roles/tmux/defaults/main.yml87 B
- ansible/roles/tmux/files/tz2.4 KB
- ansible/roles/tmux/tasks/main.yml3.6 KB
- ansible/roles/verify/defaults/main.yml35 B
- ansible/roles/verify/files/README.md1.5 KB
- ansible/roles/verify/files/tests/01-tailscale.sh1.5 KB
- ansible/roles/verify/files/tests/02-mosh.sh1.3 KB
- ansible/roles/verify/files/tests/03-caddy.sh1.5 KB
- ansible/roles/verify/files/tests/04-tmux.sh1.5 KB
- ansible/roles/verify/files/tests/05-sshd.sh1.2 KB
- ansible/roles/verify/files/tests/06-firewall.sh1.8 KB
- ansible/roles/verify/files/tests/07-storage.sh907 B
- ansible/roles/verify/files/verify.sh1.9 KB
- ansible/roles/verify/tasks/main.yml1023 B
- ansible/site.yml2.8 KB
- README.md14.8 KB
- scripts/configure.sh9.0 KB
- scripts/destroy.sh5.7 KB
- scripts/preflight.sh10.0 KB
- scripts/provision.sh23.2 KB
- scripts/verify.sh2.4 KB
SKILL.md(原文)
インストールする前に、エージェントに与えられる指示の中身を確認できます。
OCI Always Free remote dev host
Deliverable: a reachable, hardened development host that costs nothing, plus a repo of scripts and an Ansible playbook that recreates it. This skill is the genericized, distribution-ready version of the author's private dev-host playbook.
Layout
scripts/preflight.sh installs OCI CLI + Ansible + collections, checks auth (idempotent)
scripts/provision.sh VCN, IGW, security list, NSG, subnet, A1 instance (idempotent)
scripts/configure.sh renders inventory/vars, runs the playbook over SSH
scripts/verify.sh syntax check + playbook + on-host verification suite
scripts/destroy.sh terminates the instance, optionally the whole network
ansible/ the playbook (site.yml, roles/, group_vars/)
Hard rules
- Never open tcp/22 to
0.0.0.0/0.provision.shallows only the operator's detected public IP/32, andconfigure.sh --tailscale-onlyremoves even that. The host is meant to be reachable over Tailscale only. - Run the phases in order, never skip preflight.
provision.shrefuses to run without a working OCI authentication. - Two human gates. (1) Uploading the OCI API public key in the Console. (2) Opening the Tailscale login URL when no auth key is configured. Everything else is unattended. Do not fabricate either one — stop and ask.
destroy.shis destructive. Run it only on an explicit user order; it deletes the boot volume by default.- Always Free, not free trial. Resources must live in the tenancy's home region; check the current allowance before raising
OCPUS/MEMORY_GB(see Costs below). - Never put real OCIDs, IPs, tailnet names or secrets into the repo or into a commit.
0. Resolve the skill directory
SKILL_DIR=<directory containing this SKILL.md> # e.g. ~/.omp/agent/skills/oci-free-dev-env
cd "$SKILL_DIR"
[ -f .env ] || cp .env.example .env # then set the values the user chose
Ask the user only for what you cannot decide: home region if the tenancy has several, desired host name, and whether to use a Tailscale auth key now. Everything else has a working default. Ask for confirmation of Always Free assumptions when the tenancy is not new.
1. Preflight — OCI CLI, Ansible, authentication
export PATH="$HOME/bin:$HOME/.local/bin:$PATH"
scripts/preflight.sh
- Installs the OCI CLI when missing (Homebrew on macOS, Oracle's installer on Linux), installs
ansible-core+community.general, creates~/.ssh/id_ed25519when absent, then verifies authentication withoci iam region-subscription list. - Exit 2 = the human must act. Print the guide it emitted and hand these to the user:
oci setup config(asks for user OCID, tenancy OCID, region) — this also generates the API key pair.- Console → Profile → My profile → API keys → Add API key → Paste a public key → paste
cat ~/.oci/oci_api_key_public.pem. Direct link: https://cloud.oracle.com/identity/domains/my-profile/api-keys oci setup repair-file-permissions --file ~/.oci/config, then re-runscripts/preflight.sh.
- Wait for the user to confirm the key was uploaded, then re-run. Do not continue while exit code is 2.
- Browser-login alternative, no API keys:
oci session authenticate --region <home-region>, then run every script withOCI_CLI_ARGS="--auth security_token --profile <name>".
2. Provision — cloud resources
scripts/provision.sh # ~2 min when capacity is available, minutes to hours when it is not
Creates or reuses by display name: VCN, internet gateway, default-route-table route, a dedicated security list (egress only — deliberately not the VCN default list, which allows SSH from anywhere), NSG with tcp/22 restricted to the operator /32 plus udp/41641 for Tailscale direct connections, public subnet, and the VM.Standard.A1.Flex instance with the SSH key injected at launch.
OutOfHostCapacityis normal for A1 shapes. The script rotates availability domains and retriesMAX_ATTEMPTStimes (default 24 × 60 s). Report progress to the user rather than treating it as an error; a background retry loop is also acceptable (MAX_ATTEMPTS=0retries forever).LimitExceededis a different problem: the tenancy allowance is smaller than requested. LowerOCPUS/MEMORY_GB(Always Free is 2 OCPU / 12 GB since 2026-06-15; older tenancies may still hold 4 / 24).- Result:
state/devhost.env(instance OCID, public IP, NSG/subnet/VCN ids). Never commit it.
3. Configure — the Ansible stack
scripts/configure.sh
Renders ansible/inventory.ini, ansible/host_vars/<name>.yml (0600) and group_vars/all/secrets.yml (0600) from .env and the state file, waits for SSH (300 s budget), then runs site.yml. Host vars are used deliberately: inside a group_vars directory Ansible loads files in lexicographic order and the last one wins, so a generated local.yml would be overridden by main.yml. Roles and what they guarantee are listed in README.md.
- The bootstrap run reaches the host over the public IP with the
/32rule.ssh_wan_allowtherefore contains that/32and the playbook writes atcp/22ufw rule for it — that is expected at this stage, not a finding. - If no Tailscale auth key was configured, the playbook prints a
https://login.tailscale.com/...URL and the host stays unauthenticated. Give the URL to the user, wait for them to open it, then continue. Verify withssh <host> tailscale status | head -1or re-runscripts/configure.sh. - Re-running
configure.shis safe and is the right response to any partial failure.
4. Cutover — Tailscale only
scripts/configure.sh --tailscale-only
Refuses to run unless the host already reports BackendState: Running. It empties ssh_wan_allow, names the bootstrap /32 in ssh_wan_allow_remove, and re-applies the playbook, which deletes the tcp/22 ufw rule for that address. Any later configure.sh run that reaches the host over the tailnet does the same automatically, so the WAN path closes itself once Tailscale works. The host stays reachable as ssh <DEVHOST_NAME> (MagicDNS). The client must be on the tailnet: tailscale up (macOS: brew install --cask tailscale).
The cloud NSG rule for tcp/22 is not touched by the playbook: ufw already denies the port, and provision.sh only re-adds the rule when it is missing. Delete it in the Console (Networking → VCN → NSG → rules) if the cloud edge must be closed too.
5. Verify — evidence, not assurances
scripts/verify.sh
ansible-playbook --syntax-check, then the playbook, then on-host ~/.devhost-verify/verify.sh (7 tests: Tailscale, mosh, Caddy headers, tmux persistence, effective sshd -T hardening, ufw + fail2ban, storage). The suite exits 0 when nothing failed and 1 otherwise — a skipped test prints in the summary line but does not fail the run. Before the Tailscale cutover the only expected skips are 01-tailscale and 03-caddy. Any other skip (05-sshd / 06-firewall skip when passwordless sudo is unavailable) means the test could not see the system: fix the cause, do not report success.
Also prove reachability yourself before reporting success:
ssh <DEVHOST_NAME> 'hostname; tailscale ip -4; sudo ufw status | head -3; tmux ls || true'
Report to the user: host name, tailnet address, how to log in (ssh <name>, mosh <name>), where web previews appear (http://<tailnet-ip>:9000-9009 → 127.0.0.1:<port> on the host), and that scripts/destroy.sh removes everything.
6. Teardown
scripts/destroy.sh # instance + boot volume
scripts/destroy.sh --all # also VCN, subnet, NSG, security list, IGW
Confirm with the user first, then pass --yes for scripted use. Remind the user to check Block Volumes in the Console for anything left behind — a preserved volume or an idle instance keeps consuming the Always Free allowance.
Costs, limits, and the traps
| Item | Always Free allowance (current docs) |
|---|---|
| Ampere A1 compute | 1,500 OCPU-h + 9,000 GB-h per month = 2 OCPU / 12 GB continuously |
| AMD micro shapes | 2 × VM.Standard.E2.1.Micro (1/8 OCPU, 1 GB) |
| Block storage | 200 GB total, boot + block volumes combined; 5 volume backups |
| Outbound transfer | 10 TB per month |
| VCNs | 2 on free-only tenancies |
- Region: Always Free compute and block storage exist only in the tenancy's home region.
- Halving: on 2026-06-15 Oracle cut the A1 allowance from 4 OCPU / 24 GB to 2 OCPU / 12 GB. Pre-existing larger instances may be disabled. Check Governance & Administration → Limits, Quotas and Usage before assuming 4/24.
- Idle reclamation: Oracle may reclaim Always Free instances that stay under 20% CPU and network and memory (95th percentile) for 7 days. A dev host that is genuinely used is safe; a box left idle for a week is not.
- Charges: an Always Free account is refused rather than billed. A pay-as-you-go account is billed for anything above the allowance (extra OCPUs, boot volume beyond 200 GB, a second load balancer). If the user upgraded, say so plainly.
レビュー
まだレビューはありません。使ってみた感想をお寄せください。