Saltar al contenido
← Todos los posts

VM gratis para siempre en Oracle Cloud (y cómo no perderla)

Guía completa para crear, por CLI, una máquina virtual Always Free de Oracle Cloud con IP pública, mantenerla encendida 24/7 sin pagar y evitar que Oracle la reclame por inactividad.

Por Pablo Contreras32 min de lectura12 capítulos

  • oracle-cloud
  • always-free
  • oci-cli
  • vm
  • self-hosting

Costo: USD 0 al mes · Tiempo: ~1 h la primera vez · Nivel: intermedio (terminal Linux, SSH)


TL;DR

  • Qué obtienes: una VM Ubuntu 24.04 con IP pública, firewall abierto en 22/80/443 y sin fecha de vencimiento.
  • Formas gratis: ARM A1.Flex (hasta 2 OCPU y 12 GB) o AMD E2.1.Micro (1/8 OCPU, 1 GB). A1 suele estar sin capacidad; la Micro casi siempre está disponible.
  • El riesgo: Oracle puede reclamar las instancias cuyo percentil 95 de CPU queda bajo 20 % durante 7 días (si la red y, solo en A1, la memoria también quedan bajo 20 %).
  • La solución: en A1, instancia chica (la RAM usada supera el 20 %); en Micro, un keepalive de CPU de prioridad mínima.
  • Monitoreo: la VM registra su CPU cada minuto, calcula el p95 igual que Oracle y envía un reporte cada 45 min a un webhook.

Sirve para cualquier servicio liviano: un bot, una API pequeña, un servidor de notificaciones, un proxy, un cron.


Índice

#CapítuloQué resuelve
01Por qué Oracle CloudComparación con Render, Hetzner, AWS y cuándo no usarlo
02Qué incluye la capa gratuitaFree Trial vs. Always Free, cifras oficiales, duración
03Alta de la cuenta y regiónLa decisión irreversible: la región principal
04API key y CLI ociTrabajar todo desde la terminal
05Red: compartment, VCN, subred y firewallScript de red con puertos 22, 80 y 443
06Crear la instanciaScript con reintentos para A1 y plan B Micro
07Primer arranque de la VMSSH, swap, iptables de Oracle, actualizaciones
08Reclamo por inactividadLa regla exacta y el keepalive
09Medir la CPU y reporte de estadop95 como Oracle, reporte cada 45 min
10Operación y borradoEncender, apagar, respaldar, borrar todo
11Migrar de Micro a A1Cuando aparezca capacidad ARM
12Problemas y solucionesTodo lo que falló y cómo se resolvió

Convenciones

  • Valores de ejemplo: IP 203.0.113.10 (rango de documentación), región sa-santiago-1, nombre del proyecto mi-vm. Cámbialos por los tuyos.
  • Placeholders: <TENANCY_OCID>, <USER_OCID>, <FINGERPRINT>. Los OCID no son secretos; la API key privada sí.
  • Terminal: Linux o WSL2 con bash, curl, jq y uv (o pipx).
  • Fecha de los datos: octubre de 2026. Los capítulos enlazan la fuente oficial cuando existe.

oracle-cloud always-free oci-cli vm self-hosting

Por qué Oracle Cloud

El problema

  • Muchos servicios pequeños necesitan una máquina siempre encendida con IP pública: un bot, un webhook, un servidor de notificaciones, una API interna.
  • Las capas gratis de PaaS “duermen” el servicio si no recibe tráfico.
  • Las capas gratis de AWS y GCP vencen o cobran la IPv4.

Lo que ofrece Oracle Always Free

  • VM gratis sin fecha de vencimiento (no es una prueba de 12 meses).
  • IP pública incluida y 10 TB de tráfico de salida al mes.
  • Hasta 2 VM AMD Micro (1 GB) o el equivalente a 2 OCPU y 12 GB en ARM.
  • No se apaga por falta de tráfico: queda encendida 24/7.

Comparación

Precios aproximados a octubre de 2026.

OpciónCosto mensualSiempre encendidaDisco persistenteProblema
Oracle Always FreeUSD 0SíSí (hasta 200 GB)Reclamo por inactividad; alta engorrosa; A1 sin capacidad
Render FreeUSD 0No: duerme a los 15 min sin tráficoNoArranque de ~1 min; 750 h/mes; reinicios arbitrarios
Hetzner (VPS más chico)~EUR 4–5SíSíSe paga
AWS t4g.nano~USD 3 + ~USD 3,6 por la IPv4SíSí~USD 7 por una máquina mínima
PC propiaUSD 0Solo si está encendidaSíSin IP pública fija; depende de tu equipo

Render Free en detalle

Fuente: render.com/docs/free.

  • Se apaga tras 15 min sin tráfico entrante y tarda ~1 min en volver: la primera petición falla o espera.
  • 750 h al mes por workspace: un servicio 24/7 consume 744 h en un mes de 31 días, así que solo cabe uno.
  • Sin disco persistente: todo lo local se pierde en cada reinicio o redespliegue.
  • Puede reiniciar los servicios gratuitos en cualquier momento.

Útil para demos; no para algo que debe responder siempre.

Cuándo NO usar Oracle Always Free

  • Producción con clientes: Oracle puede reclamar la instancia y no documenta aviso previo.
  • Datos sin respaldo: la VM puede desaparecer.
  • Si no quieres administrar un servidor: parches, firewall y monitoreo son tu responsabilidad.

oracle-cloud always-free comparacion render hetzner aws

Qué incluye la capa gratuita

Fuente: Always Free Resources (consultada en octubre de 2026).


Free Trial vs. Always Free

Al crear la cuenta, la consola muestra:

Free Tier account · You are in a Free Trial. When your trial is over, your account is limited to Always Free resources.

Free TrialAlways Free
Qué esCrédito de USD 300 para cualquier servicioRecursos gratis sin fecha de vencimiento
DuraciónNormalmente 30 días (la página oficial no lo precisa; revisa tu consola)Indefinida, mientras la cuenta exista y el recurso no sea reclamado
Al terminarLo que no sea Always Free se detiene y luego se borraSigue funcionando
CobroNinguno, salvo que hagas Upgrade a Pay As You GoNinguno

Regla práctica: crea solo recursos con la etiqueta Always Free-eligible. Lo demás desaparece al terminar la prueba.

Cómputo

FormaCantidad gratisCPURAMRed
VM.Standard.E2.1.Micro (AMD x86)Hasta 2 instancias1/8 OCPU (con ráfagas)1 GBHasta 50 Mbps a internet
VM.Standard.A1.Flex (Ampere ARM)1.500 h OCPU + 9.000 h GB al mesEquivale a 2 OCPUEquivale a 12 GBSegún la forma
  • A1 se usa como una instancia de 2 OCPU/12 GB o como dos de 1 OCPU.
  • Las instancias Always Free solo se crean en la región principal.
  • Durante la prueba, oci limits muestra cupos mucho mayores (por ejemplo 41 OCPU y 277 GB de A1). Son del crédito, no de Always Free.

Almacenamiento y red

RecursoGratis
Block Volume (boot + datos)200 GB en total, solo región principal
Volumen de arranqueMínimo ~47–50 GB por instancia (la página es inconsistente)
Respaldos de volumen5 en total
Object Storage20 GB y 50.000 requests al mes
Tráfico de salida10 TB al mes
VCNHasta 2
Load Balancer1 flexible de 10 Mbps + 1 Network Load Balancer

Otros servicios

ServicioGratis
Monitoring500 millones de puntos ingeridos, 1.000 millones consultados
Notifications1 millón HTTPS y 1.000 emails al mes
Email Delivery3.000 emails al mes
Autonomous Database2 bases de datos de 1 OCPU y 20 GB
Vault20 versiones de clave HSM, 150 secretos

Cuánto dura

  • La cuenta y lo Always Free: sin vencimiento declarado.
  • Una instancia concreta: mientras no sea reclamada por inactividad (capítulo 08) ni la borres.
  • El crédito de prueba: ~30 días.

oracle-cloud always-free free-trial limites

Alta de la cuenta y región principal

Pasos

  1. Entra a https://signup.cloud.oracle.com/.

  2. País, nombre y correo; verifica el correo.

  3. Elige el nombre de la cuenta (cloud account name o tenancy). Te lo pedirán en cada inicio de sesión.

  4. Elige la región principal (ver abajo).

  5. Tarjeta de verificación: Oracle hace un cargo temporal que luego se reversa. No hay cobro sin Upgrade.

  6. Espera el aprovisionamiento. La pantalla dice:

    Estamos creando su cuenta; este proceso puede tardar hasta 15 minutos.

    Llegan dos correos: cuenta creada y cuenta lista (fully provisioned). Puedes intentar entrar en https://cloud.oracle.com/ antes del segundo.

La región principal: la decisión irreversible

  • No se puede cambiar después del alta.
  • Las instancias Always Free solo se crean ahí.
  • El formulario advierte:

    Las cuentas gratuitas no están disponibles en todas las regiones de datos comerciales. La disponibilidad de cada región puede cambiar a lo largo del tiempo. No intente crear varias cuentas para cambiar de región.

  • Oracle no publica qué regiones admiten cuentas gratis ni la capacidad A1 de cada una. La fuente real es el selector del formulario.

Regiones cercanas a Sudamérica

RegiónIdentificador
Colombia Central (Bogotá)sa-bogota-1
Chile Central (Santiago)sa-santiago-1
Chile West (Valparaíso)sa-valparaiso-1
Brazil East (São Paulo)sa-saopaulo-1
Brazil Southeast (Vinhedo)sa-vinhedo-1
Mexico Central (Querétaro)mx-queretaro-1
Mexico Northeast (Monterrey)mx-monterrey-1
US East (Ashburn)us-ashburn-1

Fuente: Regions and Availability Domains.

Cómo elegir

  1. Mira cuáles aparecen en el selector: las que no admiten cuenta gratis no aparecen o muestran aviso.
  2. Elige la más cercana a tus usuarios.
  3. Las regiones grandes (Ashburn, Phoenix, Frankfurt) tienen fama de estar sin capacidad A1. Es anecdótico y no verificable.

Caso real

  • Región principal: Chile Central (Santiago), con un solo dominio de disponibilidad (AD-1).
  • Sin capacidad A1 el día del alta (12 intentos). Ver capítulo 06.

oracle-cloud signup regiones home-region

API key y CLI oci

Por qué la CLI

  • Todo queda en comandos repetibles (y en esta guía).
  • Permite reintentar automáticamente la creación de la instancia cuando no hay capacidad, algo tedioso desde la consola web.
  • Oracle no usa un token simple: usa una API signing key (par RSA) asociada a tu usuario.

1. Generar la API key en la consola

  1. Ícono de perfil (arriba a la derecha) → My profile.
  2. En Resources (abajo a la izquierda) → API keys → Add API key.
  3. Generate API key pair → Download private key (.pem) → Add.
  4. Copia el bloque Configuration file preview:
[DEFAULT]
user=ocid1.user.oc1..<...>
fingerprint=aa:bb:cc:...
tenancy=ocid1.tenancy.oc1..<...>
region=sa-santiago-1
key_file=<path to your private keyfile>

2. Guardar clave y config

mkdir -p ~/.oci
mv ~/Downloads/<tu-clave>.pem ~/.oci/oci_api_key.pem

cat > ~/.oci/config <<'EOF'
[DEFAULT]
user=<USER_OCID>
fingerprint=<FINGERPRINT>
tenancy=<TENANCY_OCID>
region=sa-santiago-1
key_file=~/.oci/oci_api_key.pem
EOF

chmod 700 ~/.oci && chmod 600 ~/.oci/*

Verificar que el fingerprint corresponde a la clave:

openssl rsa -in ~/.oci/oci_api_key.pem -pubout -outform DER | openssl md5 -c

3. Instalar la CLI

uv tool install oci-cli      # o: pipx install oci-cli
oci --version

Si dice command not found: ejecuta uv tool update-shell y abre otra terminal.

4. Verificar el acceso (solo lectura)

T=$(awk -F= '/^tenancy=/{print $2}' ~/.oci/config)
oci iam region-subscription list --tenancy-id "$T" --output table

Resultado esperado:

| is-home-region | region-key | region-name   | status |
| True           | SCL        | sa-santiago-1 | READY  |

Ojo: oci iam region-subscription list sin --tenancy-id falla con Missing option(s) --tenancy-id.

5. Ver tus cupos

oci iam availability-domain list --compartment-id "$T" --query 'data[].name'
for l in standard-a1-core-count standard-a1-memory-count; do
  oci limits value list --compartment-id "$T" --service-name compute --name "$l" \
    --query 'data[].{ad:"availability-domain",value:value}' --output table
done

Seguridad

  • La clave tiene el poder de tu usuario administrador sobre toda la cuenta.
  • Nunca la pegues en un chat, repo o ticket. Guárdala solo en ~/.oci/.
  • Revócala en API keys → Delete si se filtra.
  • Para limitar el alcance: crea un usuario aparte con una política solo sobre el compartment del proyecto.

Errores frecuentes

ErrorCausa o solución
NotAuthenticatedFingerprint, user o tenancy mal copiados
Advertencia de permisosoci setup repair-file-permissions --file ~/.oci/config
Missing option(s) --tenancy-idEse comando exige el tenancy explícito

oci-cli api-key autenticacion

Red: compartment, VCN, subred y firewall

Qué se crea

RecursoPara qué
CompartmentAísla el proyecto del resto de la cuenta; borrarlo limpia todo
VCN 10.0.0.0/16Red privada virtual
Internet GatewaySalida y entrada a internet
Ruta 0.0.0.0/0 → gatewayEn la tabla de rutas por defecto de la VCN
Security ListEntrada solo por 22 (SSH), 80 (HTTP) y 443 (HTTPS) + ICMP de fragmentación
Subred pública 10.0.0.0/24Donde vive la VM, con IP pública

Script: oci-crear-red.sh

Corre en tu PC con la CLI configurada (capítulo 04). Imprime los OCID listos para eval.

#!/usr/bin/env bash
# Crea compartment, VCN, internet gateway, ruta, security list (22/80/443) y subred pública.
# Uso: eval "$(bash oci-crear-red.sh mi-vm)"
set -euo pipefail
NAME="${1:-mi-vm}"
LABEL="${NAME//-/}"                       # dns-label: solo letras y números
T=$(awk -F= '/^tenancy=/{print $2}' ~/.oci/config)

# 1. Compartment
C=$(oci iam compartment create --compartment-id "$T" --name "$NAME" \
  --description "Proyecto $NAME" --wait-for-state ACTIVE --query 'data.id' --raw-output)
sleep 20                                  # el compartment tarda en propagarse

# 2. VCN
V=$(oci network vcn create -c "$C" --cidr-blocks '["10.0.0.0/16"]' --display-name "$NAME-vcn" \
  --dns-label "${LABEL}vcn" --wait-for-state AVAILABLE --query 'data.id' --raw-output)

# 3. Internet Gateway
IG=$(oci network internet-gateway create -c "$C" --vcn-id "$V" --is-enabled true \
  --display-name "$NAME-igw" --wait-for-state AVAILABLE --query 'data.id' --raw-output)

# 4. Ruta por defecto hacia el gateway
RT=$(oci network vcn get --vcn-id "$V" --query 'data."default-route-table-id"' --raw-output)
oci network route-table update --rt-id "$RT" --force --wait-for-state AVAILABLE \
  --route-rules "[{\"destination\":\"0.0.0.0/0\",\"destinationType\":\"CIDR_BLOCK\",\"networkEntityId\":\"$IG\"}]" >/dev/null

# 5. Security List: 22, 80, 443 y ICMP 3/4
SL=$(oci network vcn get --vcn-id "$V" --query 'data."default-security-list-id"' --raw-output)
ING='[
 {"protocol":"6","source":"0.0.0.0/0","tcpOptions":{"destinationPortRange":{"min":22,"max":22}}},
 {"protocol":"6","source":"0.0.0.0/0","tcpOptions":{"destinationPortRange":{"min":80,"max":80}}},
 {"protocol":"6","source":"0.0.0.0/0","tcpOptions":{"destinationPortRange":{"min":443,"max":443}}},
 {"protocol":"1","source":"0.0.0.0/0","icmpOptions":{"type":3,"code":4}}]'
oci network security-list update --security-list-id "$SL" --force --wait-for-state AVAILABLE \
  --ingress-security-rules "$ING" \
  --egress-security-rules '[{"protocol":"all","destination":"0.0.0.0/0"}]' >/dev/null

# 6. Subred pública
SN=$(oci network subnet create -c "$C" --vcn-id "$V" --cidr-block 10.0.0.0/24 \
  --display-name "$NAME-subnet" --dns-label "${LABEL}sub" --wait-for-state AVAILABLE \
  --query 'data.id' --raw-output)

printf 'export C=%s\nexport V=%s\nexport IG=%s\nexport RT=%s\nexport SL=%s\nexport SN=%s\n' \
  "$C" "$V" "$IG" "$RT" "$SL" "$SN"

Uso:

eval "$(bash oci-crear-red.sh mi-vm)"
echo "$C $SN"     # guárdalos: los usan los capítulos siguientes

Tarda ~2 minutos.

Notas

  • --force en los update evita la confirmación interactiva; esos update reemplazan la lista completa de reglas.
  • Protocolo 6 = TCP, 1 = ICMP. La regla ICMP tipo 3 código 4 permite Path MTU Discovery.
  • --wait-for-state hace que cada paso espere a que el recurso exista.
  • Para abrir otro puerto, agrega una regla al JSON ING y vuelve a ejecutar el security-list update.

Las dos capas de firewall

En Oracle hay dos firewalls y ambos deben permitir el puerto:

  1. Security List de la VCN (este capítulo).
  2. iptables dentro de la VM: la imagen Ubuntu de Oracle trae un REJECT para todo tráfico entrante nuevo salvo SSH. Se abre en el capítulo 07.

oracle-cloud vcn security-list firewall oci-cli

Crear la instancia

A1 o Micro

VM.Standard.A1.Flex (ARM)VM.Standard.E2.1.Micro (AMD)
CPU / RAM gratisHasta 2 OCPU / 12 GB1/8 OCPU / 1 GB
DisponibilidadSuele dar “Out of host capacity”Normalmente disponible
Regla de reclamoCPU, red y memoriaSolo CPU y red
Evitar el reclamoInstancia chica: la RAM usada ya supera el 20 %Necesita un keepalive de CPU
RecomendaciónPrimera opciónPlan B cuando A1 no tiene capacidad

Truco para A1: créala con 1 OCPU y 1 GB. Ubuntu más un servicio liviano usan ~400 MB, más del 20 % de la RAM: nunca se considera inactiva y no hace falta generar carga artificial. Ver capítulo 08.

1. Clave SSH

ssh-keygen -t ed25519 -N "" -C "oci-mi-vm" -f ~/.ssh/oci_mi_vm

2. Script: oci-crear-instancia.sh

Requiere C y SN exportados (capítulo 05).

#!/usr/bin/env bash
# Crea la instancia Always Free con Ubuntu 24.04.
# Uso: eval "$(bash oci-crear-instancia.sh a1)"     → A1 1 OCPU / 1 GB, reintenta cada 2 min
#      eval "$(bash oci-crear-instancia.sh micro)"  → VM.Standard.E2.1.Micro
set -uo pipefail
KIND="${1:-a1}"
NAME="${NAME:-mi-vm}"
SSH_PUB="${SSH_PUB:-$HOME/.ssh/oci_mi_vm.pub}"
MAX_TRIES="${MAX_TRIES:-720}"             # 720 × 2 min = 24 h
: "${C:?exporta C}" "${SN:?exporta SN}"

AD=$(oci iam availability-domain list -c "$C" --query 'data[0].name' --raw-output)
if [ "$KIND" = "a1" ]; then
  SHAPE=VM.Standard.A1.Flex; CFG=(--shape-config '{"ocpus":1,"memoryInGBs":1}')
else
  SHAPE=VM.Standard.E2.1.Micro; CFG=(); MAX_TRIES=1
fi

# Imagen Ubuntu 24.04 más reciente compatible con la forma (aarch64 para A1, x86_64 para Micro)
IMG=$(oci compute image list -c "$C" --operating-system "Canonical Ubuntu" \
  --operating-system-version "24.04" --shape "$SHAPE" --sort-by TIMECREATED --sort-order DESC \
  --limit 1 --query 'data[0].id' --raw-output)

for i in $(seq 1 "$MAX_TRIES"); do
  OUT=$(oci compute instance launch -c "$C" --availability-domain "$AD" --shape "$SHAPE" "${CFG[@]}" \
    --image-id "$IMG" --subnet-id "$SN" --assign-public-ip true \
    --display-name "$NAME" --hostname-label "${NAME//-/}" --ssh-authorized-keys-file "$SSH_PUB" \
    --wait-for-state RUNNING --max-wait-seconds 600 --query 'data.id' --raw-output 2>&1)
  if [[ "$OUT" == ocid1.instance* ]]; then
    IP=$(oci compute instance list-vnics --instance-id "$OUT" --query 'data[0]."public-ip"' --raw-output)
    printf 'export I=%s\nexport IP=%s\n' "$OUT" "$IP"
    exit 0
  fi
  echo "$(date '+%F %T') intento $i: $(echo "$OUT" | grep -o '"message": "[^"]*"' | head -1)" >&2
  [ "$i" -lt "$MAX_TRIES" ] && sleep 120
done
exit 1

3. Uso

Primero intenta A1 un rato; si no hay capacidad, Micro:

MAX_TRIES=6 bash oci-crear-instancia.sh a1 > instancia.env || bash oci-crear-instancia.sh micro > instancia.env
eval "$(cat instancia.env)"; echo "$I $IP"

Para esperar A1 en segundo plano (hasta 24 h):

nohup bash oci-crear-instancia.sh a1 > instancia-a1.env 2> intentos-a1.log &
tail -f intentos-a1.log

“Out of host capacity”

2026-10-11 15:52:04 intento 1: "message": "Out of host capacity."
  • No es un error tuyo: la región no tiene hosts ARM libres para la capa gratis.
  • No hay cola: el que pide justo cuando se libera, gana. Reintentar cada pocos minutos durante horas o días suele funcionar.
  • Caso real en Santiago: 12 intentos de A1 fallidos; la Micro se creó al primer intento en ~2 minutos.

Comprobar

oci compute instance get --instance-id "$I" --query 'data.{estado:"lifecycle-state",forma:shape}'

oracle-cloud compute a1-flex e2-micro out-of-capacity

Primer arranque de la VM

1. Primer SSH

ssh -i ~/.ssh/oci_mi_vm ubuntu@203.0.113.10
  • Los primeros 1–2 minutos responde Connection refused: cloud-init aún está arrancando. Reintenta.
  • El usuario de la imagen Ubuntu es ubuntu (con sudo sin contraseña).

Atajo en ~/.ssh/config:

Host mi-vm
  HostName 203.0.113.10
  User ubuntu
  IdentityFile ~/.ssh/oci_mi_vm

2. Lo que trae la imagen

En una Micro recién creada:

x86_64 · Ubuntu 24.04.5 LTS · 954 MB de RAM (~400 MB usados al arrancar) · sin swap
-P INPUT ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited

El REJECT final de iptables bloquea 80 y 443 aunque la Security List los permita. Es el error más común en Oracle.

3. Script: vm-base.sh

Swap, puertos 80/443 en iptables y parches de seguridad automáticos. Idempotente.

#!/usr/bin/env bash
# Preparación base de una VM Ubuntu 24.04 de Oracle. Se ejecuta EN LA VM con sudo.
# Uso desde tu PC: ssh mi-vm 'sudo bash -s' < vm-base.sh
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
PORTS="${PORTS:-80 443}"

# 1. Swap de 1 GB (la Micro tiene 1 GB de RAM; apt y otros procesos pueden quedarse sin memoria)
if ! swapon --show | grep -q /swapfile; then
  fallocate -l 1G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile
  grep -q /swapfile /etc/fstab || echo '/swapfile none swap sw 0 0' >> /etc/fstab
fi

# 2. iptables: abrir los puertos ANTES del REJECT final (posición 5) y persistir
for p in $PORTS; do
  iptables -C INPUT -p tcp -m state --state NEW --dport "$p" -j ACCEPT 2>/dev/null \
    || iptables -I INPUT 5 -p tcp -m state --state NEW --dport "$p" -j ACCEPT
done
netfilter-persistent save

# 3. Parches de seguridad automáticos
apt-get update -q
apt-get install -y -q unattended-upgrades

iptables -S INPUT
swapon --show

Uso:

ssh mi-vm 'sudo bash -s' < vm-base.sh
ssh mi-vm 'sudo env PORTS="80 443 8080" bash -s' < vm-base.sh   # otros puertos

Cada puerto nuevo también debe abrirse en la Security List (capítulo 05).

4. Comprobar desde afuera

Con cualquier servicio escuchando en el puerto (por ejemplo sudo python3 -m http.server 80):

curl -I http://203.0.113.10

Si se queda colgado: revisa la Security List y iptables. Si responde Connection refused: el firewall está bien, pero no hay nada escuchando.

oracle-cloud ssh iptables swap ubuntu

Reclamo por inactividad: la regla y cómo evitarla

La regla oficial

Texto exacto de Always Free Resources:

Oracle will deem virtual machine and bare metal compute instances as idle if, during a 7-day period, the following are true:

  • CPU utilization for the 95th percentile is less than 20%
  • Network utilization is less than 20%
  • Memory utilization is less than 20% (applies to A1 shapes only)

Idle Always Free compute instances may be reclaimed by Oracle.

Cómo leerla

  • Las condiciones van unidas por “y”: la instancia es inactiva solo si todas se cumplen. Basta con que una supere el 20 % para no serlo.
  • Percentil 95 de 7 días: se ordenan todas las mediciones de la semana y se toma la que deja el 95 % por debajo. Si la CPU pasa de 20 % durante algo más del 5 % del tiempo, el p95 supera 20 %.
  • La memoria solo cuenta en A1. En la Micro solo cuentan CPU y red.
  • Un servicio liviano (un bot, un webhook, un servidor de notificaciones) casi no usa CPU ni red: para esta regla, casi siempre cuenta como inactivo.
  • La documentación no dice si avisa antes de reclamar ni cómo.
  • Pasar la cuenta a Pay As You Go se recomienda en foros, pero la documentación no dice que exima del reclamo.
  • Una instancia detenida tampoco usa CPU: no la dejes apagada días.

Estrategias

EstrategiaAplica aCómoEvaluación
Instancia chicaA11 OCPU y 1 GB: el uso normal (~400 MB) supera el 20 % de RAMLimpia, sin carga artificial. La mejor
Keepalive de CPUMicro (y A1 si se quiere)Cron que usa CPU poco más de un minuto cada pocos minutosFunciona; es carga artificial (zona gris frente a los términos)
Tráfico de redAmbasGenerar tráfico sostenidoDesperdicia ancho de banda; descartada

El keepalive (Micro)

ssh mi-vm "echo \"*/10 * * * * root timeout 75 nice -n 19 sh -c 'cat /dev/urandom | sha256sum >/dev/null' 2>/dev/null || true\" | sudo tee /etc/cron.d/oci-keepalive"

Contenido de /etc/cron.d/oci-keepalive:

*/10 * * * * root timeout 75 nice -n 19 sh -c 'cat /dev/urandom | sha256sum >/dev/null' 2>/dev/null || true
PartePor qué
*/10Cada 10 minutos
timeout 7575 s de carga ≈ 12,5 % del tiempo, más que el 5 % que necesita el p95; nunca queda corriendo
nice -n 19Prioridad mínima: tus servicios siempre tienen la CPU primero
sha256sum de /dev/urandomUsa CPU sin tocar disco ni red

Evidencia

CPU máxima por minuto según Oracle Monitoring (UTC):

21:11  50%   21:12  88%   ← keepalive
21:13   0%  …  21:20   0%
21:21  56%   21:22  47%   ← keepalive

Percentil 95 de la última hora: 49,9 % según Oracle y 49 % según el registro local de la VM. Muy por encima del 20 %.

No te confundas con el “0 %”

Ver CPU 0 % en un momento dado no significa riesgo: es la foto de un instante sin keepalive. Lo que importa es el p95 de 7 días. El capítulo 09 lo mide y lo reporta.

Si la reclaman

  • La IP pública cambia al recrear la instancia: actualiza tu DNS.
  • Lo que estaba en el disco se pierde: guarda tus scripts de instalación y respaldos fuera de la VM (capítulo 10).
  • El reporte de estado deja de llegar: la ausencia de reporte es la alarma.

oracle-cloud always-free idle-reclamation keepalive p95

Medir la CPU como Oracle y reporte de estado

Dos fuentes

FuenteDónde correPara qué
Oracle MonitoringAPI de Oracle, desde tu PC con ociLa cifra que ve Oracle
Registro localLa VM, cada minutoQue la VM informe sola su p95

Oracle Monitoring desde la CLI

El agente de cómputo publica CpuUtilization en el namespace oci_computeagent.

CPU máxima por minuto de la última hora y media:

S=$(date -u -d '-90 min' +%Y-%m-%dT%H:%M:%SZ); E=$(date -u +%Y-%m-%dT%H:%M:%SZ)
oci monitoring metric-data summarize-metrics-data -c "$C" --namespace oci_computeagent \
  --query-text 'CpuUtilization[1m].max()' --start-time "$S" --end-time "$E" \
  --query 'data[0]."aggregated-datapoints"[].[timestamp,value]' --output json \
  | jq -r '.[] | "\(.[0][11:16]) UTC  \(.[1]|floor)%"'

Percentil 95 (para la ventana real de Oracle usa -7 days y [1d]: obtienes un p95 por día, porque MQL no acepta intervalos mayores a 1d):

S=$(date -u -d '-60 min' +%Y-%m-%dT%H:%M:%SZ)
oci monitoring metric-data summarize-metrics-data -c "$C" --namespace oci_computeagent \
  --query-text 'CpuUtilization[1h].percentile(0.95)' --start-time "$S" --end-time "$E" \
  --query 'data[0]."aggregated-datapoints"[-1].value' --raw-output

Otras métricas del namespace: MemoryUtilization, NetworksBytesIn, NetworksBytesOut. En la consola: instancia → Metrics.

Reporte de estado desde la VM

La VM registra su CPU cada minuto, calcula el p95 de 24 h y de 7 días, revisa RAM, disco y servicios, y envía un reporte cada 45 min a un webhook (cualquier URL que acepte un POST: Slack, Discord, Telegram, un endpoint propio…).

  • Por qué desde la VM: no depende de que tu PC esté encendida y ve datos internos.
  • Límite: si la VM muere, no envía nada. Si pasan más de 45 min sin reporte, esa es la alarma.

Ejemplo de reporte:

VM OK (16:43)
CPU p95 24h 49% · 7d 49% (mín. 20%) · ahora 0% (load 0.00 0.00 0.01)
RAM 409/954 MB (42%) · swap 0 MB · disco 8%
servicios activos: cron · encendida 45 minutes

Script del reporte: vm-report.sh

#!/usr/bin/env bash
# Reporte de estado de la VM. Lo ejecuta el timer systemd cada 45 min.
# Config en /etc/vm-report.env (solo root):
#   REPORT_WEBHOOK=https://...        URL que recibe el POST
#   REPORT_FORMAT=text|slack|discord  text: cuerpo plano; slack/discord: JSON
#   REPORT_AUTH="Bearer ..."          opcional, header Authorization
#   SERVICES="cron nginx"             servicios systemd a vigilar
#   CHECK_URL=https://...             opcional, URL pública que debe responder 2xx
#   TZ_REPORT=America/Guayaquil
set -u
. /etc/vm-report.env
: "${REPORT_FORMAT:=text}" "${SERVICES:=cron}" "${TZ_REPORT:=UTC}"
fails=()

# CPU: actual (5 s), carga y p95 del registro por minuto (/var/log/cpu-minute.log: "epoch pct")
cpu=$((100 - $(vmstat 5 2 | tail -1 | awk '{print $15}')))
load=$(cut -d' ' -f1-3 /proc/loadavg)
p95() {
  awk -v since="$(( $(date +%s) - $1 ))" '$1 >= since {print $2}' /var/log/cpu-minute.log 2>/dev/null \
    | sort -n | awk '{a[NR]=$1} END {if (NR) print a[int(NR*0.95 + 0.999)]; else print "?"}'
}
p95_24h=$(p95 86400); p95_7d=$(p95 604800)
[ "$p95_7d" = "?" ] || [ "$p95_7d" -ge 20 ] || fails+=("CPU p95 7d ${p95_7d}% (Oracle podría reclamarla)")

# RAM, swap, disco
read -r mem_total mem_used <<<"$(free -m | awk '/^Mem:/{print $2, $3}')"
mem_pct=$((mem_used * 100 / mem_total))
swap_used=$(free -m | awk '/^Swap:/{print $3}')
disk_pct=$(df --output=pcent / | tail -1 | tr -dc '0-9')
[ "$mem_pct" -lt 90 ] || fails+=("RAM ${mem_pct}%")
[ "$disk_pct" -lt 85 ] || fails+=("disco ${disk_pct}%")

# Servicios
for s in $SERVICES; do systemctl is-active --quiet "$s" || fails+=("$s inactivo"); done

# URL pública (opcional)
if [ -n "${CHECK_URL:-}" ]; then
  code=$(curl -s -o /dev/null -m 10 -w '%{http_code}' "$CHECK_URL")
  [[ "$code" == 2* ]] || fails+=("$CHECK_URL respondió $code")
fi

reboot=""; [ -f /var/run/reboot-required ] && reboot=" · requiere reinicio"
now=$(TZ="$TZ_REPORT" date '+%H:%M')
body="CPU p95 24h ${p95_24h}% · 7d ${p95_7d}% (mín. 20%) · ahora ${cpu}% (load ${load})
RAM ${mem_used}/${mem_total} MB (${mem_pct}%) · swap ${swap_used} MB · disco ${disk_pct}%
servicios activos: ${SERVICES} · encendida $(uptime -p | sed 's/^up //')${reboot}"

if [ ${#fails[@]} -eq 0 ]; then title="VM OK ($now)"
else title="FALLO VM ($now)"; body="$(IFS='; '; echo "${fails[*]}")
$body"; fi

case "$REPORT_FORMAT" in
  slack)   payload=$(jq -n --arg t "*$title*"$'\n'"$body" '{text: $t}');      ctype=application/json ;;
  discord) payload=$(jq -n --arg t "**$title**"$'\n'"$body" '{content: $t}'); ctype=application/json ;;
  *)       payload="$title"$'\n'"$body";                                       ctype=text/plain ;;
esac
curl -fsS -m 10 -o /dev/null -H "Content-Type: $ctype" \
  ${REPORT_AUTH:+-H "Authorization: $REPORT_AUTH"} -d "$payload" "$REPORT_WEBHOOK"

Instalador: vm-install-report.sh

Copia vm-report.sh a la VM y luego ejecuta este instalador allí:

#!/usr/bin/env bash
# Instala el registro de CPU por minuto y el timer del reporte. Se ejecuta EN LA VM con sudo.
set -e
install -m 0755 /tmp/vm-report.sh /usr/local/bin/vm-report

# Configuración (edítala después con sudoedit /etc/vm-report.env)
if [ ! -f /etc/vm-report.env ]; then
  umask 077
  cat > /etc/vm-report.env <<EOF
REPORT_WEBHOOK=${REPORT_WEBHOOK:?falta REPORT_WEBHOOK}
REPORT_FORMAT=${REPORT_FORMAT:-text}
SERVICES="${SERVICES:-cron}"
CHECK_URL=${CHECK_URL:-}
TZ_REPORT=${TZ_REPORT:-UTC}
EOF
fi

# Registro de CPU por minuto (promedio de 55 s); poda diaria a 8 días
cat > /etc/cron.d/cpu-minute <<'EOF'
* * * * * root echo "$(date +\%s) $((100 - $(vmstat 55 2 | tail -1 | awk '{print $15}')))" >> /var/log/cpu-minute.log
17 3 * * * root f=/var/log/cpu-minute.log; awk -v s=$(( $(date +\%s) - 691200 )) '$1>=s' $f > $f.tmp && mv $f.tmp $f
EOF

cat > /etc/systemd/system/vm-report.service <<'EOF'
[Unit]
Description=Reporte de estado de la VM
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/vm-report
EOF

cat > /etc/systemd/system/vm-report.timer <<'EOF'
[Unit]
Description=Reporte de estado de la VM cada 45 minutos

[Timer]
OnBootSec=3min
OnUnitActiveSec=45min

[Install]
WantedBy=timers.target
EOF

apt-get install -y -q jq >/dev/null
systemctl daemon-reload
systemctl enable --now vm-report.timer
systemctl start vm-report.service
systemctl list-timers vm-report.timer --no-pager | sed -n 2p

Uso desde tu PC:

scp vm-report.sh mi-vm:/tmp/
ssh mi-vm "sudo env REPORT_WEBHOOK='https://hooks.slack.com/services/...' REPORT_FORMAT=slack \
  SERVICES='cron' TZ_REPORT=America/Guayaquil bash -s" < vm-install-report.sh

Para agregar la cabecera de autenticación, escribe REPORT_AUTH="Bearer <token>" en /etc/vm-report.env con sudoedit.

Detalles que importan

  • vmstat 55 2: el segundo renglón es el promedio de 55 s; la columna 15 es idle.
  • % en cron se escapa como \%.
  • Timer de systemd y no cron: */45 en cron dispara a :00 y :45 (huecos de 15 min). OnUnitActiveSec=45min cuenta desde la última ejecución y OnBootSec=3min envía un reporte tras cada arranque.
  • Coincidencia medida: p95 local 49 % vs. Oracle 49,9 % en la misma hora.
  • Mientras el registro tenga menos de 7 días, la cifra de “7d” cubre solo lo que haya.

Operar el reporte

sudo systemctl start vm-report.service       # reporte ahora
systemctl list-timers vm-report.timer        # próxima ejecución
journalctl -u vm-report -n 20                # últimas ejecuciones
sudo systemctl disable --now vm-report.timer # desactivar

oracle-cloud monitoring p95 systemd-timer webhook

Operación y borrado

Estado de la instancia

oci compute instance get --instance-id "$I" --query 'data."lifecycle-state"' --raw-output   # RUNNING
oci compute instance list -c "$C" --query 'data[].{n:"display-name",s:"lifecycle-state",forma:shape}' --output table

Encender, apagar, reiniciar

oci compute instance action --instance-id "$I" --action SOFTRESET   # reinicio ordenado
oci compute instance action --instance-id "$I" --action STOP
oci compute instance action --instance-id "$I" --action START

Una instancia detenida no usa CPU y cuenta como inactiva para el reclamo.

Actualizaciones

  • unattended-upgrades instala los parches de seguridad por sí solo.
  • Si el reporte dice “requiere reinicio”:
ssh mi-vm 'sudo apt-get update && sudo apt-get -y upgrade && sudo reboot'

Todo lo que esté habilitado (enable) en systemd (servicios, timer del reporte) y los crons arrancan solos.

Respaldo

La VM puede ser reclamada. Respalda fuera de ella:

  • Scripts de instalación: en un repo; recrean el servidor en minutos.
  • Datos y configuración: lo que tu servicio guarde, más /etc/cron.d/oci-keepalive y /etc/vm-report.env.
ssh mi-vm 'sudo tar czf - /etc/cron.d /etc/vm-report.env /ruta/de/tus/datos' > respaldo-$(date +%F).tgz

Oracle también permite respaldar el volumen de arranque (5 respaldos gratis):

AD=$(oci iam availability-domain list -c "$C" --query 'data[0].name' --raw-output)
BV=$(oci compute boot-volume-attachment list -c "$C" --availability-domain "$AD" --instance-id "$I" \
  --query 'data[0]."boot-volume-id"' --raw-output)
oci bv boot-volume-backup create --boot-volume-id "$BV" --display-name "mi-vm-$(date +%F)"

Ver la IP pública

oci compute instance list-vnics --instance-id "$I" --query 'data[0]."public-ip"' --raw-output

Borrar todo

Orden: primero lo que depende de otros recursos.

oci compute instance terminate --instance-id "$I" --preserve-boot-volume false --force --wait-for-state TERMINATED
oci network subnet delete --subnet-id "$SN" --force --wait-for-state TERMINATED
oci network route-table update --rt-id "$RT" --route-rules '[]' --force
oci network internet-gateway delete --ig-id "$IG" --force --wait-for-state TERMINATED
oci network vcn delete --vcn-id "$V" --force --wait-for-state TERMINATED
oci iam compartment delete --compartment-id "$C" --force     # puede tardar varios minutos

Además:

  • Borra los registros DNS que apuntaban a la IP.
  • Revoca la API key en la consola (My profile → API keys → Delete) si ya no la usarás.
  • Borra la clave SSH local: rm -f ~/.ssh/oci_mi_vm ~/.ssh/oci_mi_vm.pub.

oracle-cloud operacion backup teardown

Migrar de Micro a A1

Por qué migrar

  • A1 con 1 GB evita el reclamo sin carga artificial: la memoria usada ya supera el 20 %.
  • Más CPU y RAM (hasta 2 OCPU y 12 GB gratis).
  • Se elimina el keepalive.

Paso 1: conseguir capacidad

Deja el script del capítulo 06 reintentando en segundo plano:

NAME=mi-vm-a1 nohup bash oci-crear-instancia.sh a1 > instancia-a1.env 2> intentos-a1.log &

Al conseguirla, guarda primero el OCID de la Micro (lo usa el paso 6) y luego carga los datos de la A1:

I_MICRO=$I
eval "$(cat instancia-a1.env)"   # deja I e IP de la nueva instancia

Paso 2: preparar la A1

ssh -i ~/.ssh/oci_mi_vm ubuntu@$IP 'sudo bash -s' < vm-base.sh       # capítulo 07
  • La arquitectura es arm64: instala tu servicio con paquetes o imágenes para ARM.
  • No instales el keepalive.

Paso 3: copiar datos

ssh mi-vm 'sudo tar czf - /ruta/de/tus/datos' | ssh -i ~/.ssh/oci_mi_vm ubuntu@$IP 'sudo tar xzf - -C /'

Paso 4: reporte de estado

Instálalo como en el capítulo 09. En A1 el control relevante es la memoria: cambia la línea del p95 de CPU por:

[ "$mem_pct" -ge 20 ] || fails+=("RAM ${mem_pct}% (Oracle podría reclamarla)")

Paso 5: cambiar el DNS

Actualiza los registros A a la IP nueva. Con TTL de 300 s se propaga en ~5 min.

Paso 6: borrar la Micro

oci compute instance terminate --instance-id "$I_MICRO" --preserve-boot-volume false --force

Tiempo total estimado: ~15 minutos una vez conseguida la capacidad.

oracle-cloud a1-flex migracion arm64

Problemas y soluciones

Alta y CLI

ProblemaCausaSolución
No aparece la región que quieroNo admite cuentas gratis o cambió la disponibilidadElegir otra de la lista; la región no se cambia después
NotAuthenticatedFingerprint, user o tenancy mal copiados en ~/.oci/configVolver a copiar el Configuration file preview y verificar el fingerprint con openssl
Missing option(s) --tenancy-idiam region-subscription list exige el tenancy--tenancy-id "$(awk -F= '/^tenancy=/{print $2}' ~/.oci/config)"
Advertencia de permisos del configArchivos legibles por otrosoci setup repair-file-permissions --file ~/.oci/config
oci: command not found tras instalarPATH de uv no cargadouv tool update-shell y abrir otra terminal

Red e instancia

ProblemaCausaSolución
NotAuthorizedOrNotFound justo después de crear el compartmentPropagaciónsleep 20 antes de crear la VCN
”Out of host capacity” en A1Sin hosts ARM libres para la capa gratisReintentar en bucle cada 2 min o usar Micro
Cupos de A1 enormes en oci limitsSon del crédito de pruebaRespetar 2 OCPU / 12 GB de Always Free
SSH Connection refusedcloud-init aún arrancandoEsperar 1–2 min y reintentar
80/443 no responden aunque la Security List los permiteiptables de la imagen con REJECT finalInsertar el ACCEPT antes del REJECT y netfilter-persistent save
apt muere por falta de memoria en la Micro1 GB sin swapSwap de 1 GB (capítulo 07)

Reclamo y monitoreo

ProblemaCausaSolución
Un reporte dice “CPU 0 %“Foto de un instanteMirar el p95 de 7 días
En Micro la RAM alta no protegeLa regla de memoria solo aplica a A1Keepalive de CPU
”Cada 45 min” con cron dispara a :00 y :45*/45 no es un intervalo realTimer de systemd con OnUnitActiveSec=45min
Una línea de cron con % se corta% es especial en crontabEscaparlo como \%
La VM caída no avisaNo puede reportar si está muertaLa ausencia de reporte por más de 45 min es la alarma
Instancia reclamadap95 de CPU (y red; en A1 también memoria) bajo 20 % por 7 díasRecrearla con los scripts; IP nueva → actualizar DNS

troubleshooting oracle-cloud

↑ Volver arriba