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 AMDE2.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ítulo | Qué resuelve |
|---|---|---|
| 01 | Por qué Oracle Cloud | Comparación con Render, Hetzner, AWS y cuándo no usarlo |
| 02 | Qué incluye la capa gratuita | Free Trial vs. Always Free, cifras oficiales, duración |
| 03 | Alta de la cuenta y región | La decisión irreversible: la región principal |
| 04 | API key y CLI oci | Trabajar todo desde la terminal |
| 05 | Red: compartment, VCN, subred y firewall | Script de red con puertos 22, 80 y 443 |
| 06 | Crear la instancia | Script con reintentos para A1 y plan B Micro |
| 07 | Primer arranque de la VM | SSH, swap, iptables de Oracle, actualizaciones |
| 08 | Reclamo por inactividad | La regla exacta y el keepalive |
| 09 | Medir la CPU y reporte de estado | p95 como Oracle, reporte cada 45 min |
| 10 | Operación y borrado | Encender, apagar, respaldar, borrar todo |
| 11 | Migrar de Micro a A1 | Cuando aparezca capacidad ARM |
| 12 | Problemas y soluciones | Todo lo que falló y cómo se resolvió |
Convenciones
- Valores de ejemplo: IP
203.0.113.10(rango de documentación), regiónsa-santiago-1, nombre del proyectomi-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,jqyuv(opipx). - 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ón | Costo mensual | Siempre encendida | Disco persistente | Problema |
|---|---|---|---|---|
| Oracle Always Free | USD 0 | Sí | Sí (hasta 200 GB) | Reclamo por inactividad; alta engorrosa; A1 sin capacidad |
| Render Free | USD 0 | No: duerme a los 15 min sin tráfico | No | Arranque de ~1 min; 750 h/mes; reinicios arbitrarios |
| Hetzner (VPS más chico) | ~EUR 4–5 | Sí | Sí | Se paga |
AWS t4g.nano | ~USD 3 + ~USD 3,6 por la IPv4 | Sí | Sí | ~USD 7 por una máquina mínima |
| PC propia | USD 0 | Solo si está encendida | Sí | 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 Trial | Always Free | |
|---|---|---|
| Qué es | Crédito de USD 300 para cualquier servicio | Recursos gratis sin fecha de vencimiento |
| Duración | Normalmente 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 terminar | Lo que no sea Always Free se detiene y luego se borra | Sigue funcionando |
| Cobro | Ninguno, salvo que hagas Upgrade a Pay As You Go | Ninguno |
Regla práctica: crea solo recursos con la etiqueta Always Free-eligible. Lo demás desaparece al terminar la prueba.
Cómputo
| Forma | Cantidad gratis | CPU | RAM | Red |
|---|---|---|---|---|
VM.Standard.E2.1.Micro (AMD x86) | Hasta 2 instancias | 1/8 OCPU (con ráfagas) | 1 GB | Hasta 50 Mbps a internet |
VM.Standard.A1.Flex (Ampere ARM) | 1.500 h OCPU + 9.000 h GB al mes | Equivale a 2 OCPU | Equivale a 12 GB | Segú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 limitsmuestra cupos mucho mayores (por ejemplo 41 OCPU y 277 GB de A1). Son del crédito, no de Always Free.
Almacenamiento y red
| Recurso | Gratis |
|---|---|
| Block Volume (boot + datos) | 200 GB en total, solo región principal |
| Volumen de arranque | Mínimo ~47–50 GB por instancia (la página es inconsistente) |
| Respaldos de volumen | 5 en total |
| Object Storage | 20 GB y 50.000 requests al mes |
| Tráfico de salida | 10 TB al mes |
| VCN | Hasta 2 |
| Load Balancer | 1 flexible de 10 Mbps + 1 Network Load Balancer |
Otros servicios
| Servicio | Gratis |
|---|---|
| Monitoring | 500 millones de puntos ingeridos, 1.000 millones consultados |
| Notifications | 1 millón HTTPS y 1.000 emails al mes |
| Email Delivery | 3.000 emails al mes |
| Autonomous Database | 2 bases de datos de 1 OCPU y 20 GB |
| Vault | 20 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
-
Entra a https://signup.cloud.oracle.com/.
-
País, nombre y correo; verifica el correo.
-
Elige el nombre de la cuenta (cloud account name o tenancy). Te lo pedirán en cada inicio de sesión.
-
Elige la región principal (ver abajo).
-
Tarjeta de verificación: Oracle hace un cargo temporal que luego se reversa. No hay cobro sin Upgrade.
-
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ón | Identificador |
|---|---|
| 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
- Mira cuáles aparecen en el selector: las que no admiten cuenta gratis no aparecen o muestran aviso.
- Elige la más cercana a tus usuarios.
- 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
- Ícono de perfil (arriba a la derecha) → My profile.
- En Resources (abajo a la izquierda) → API keys → Add API key.
- Generate API key pair → Download private key (
.pem) → Add. - 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 listsin--tenancy-idfalla conMissing 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
| Error | Causa o solución |
|---|---|
NotAuthenticated | Fingerprint, user o tenancy mal copiados |
| Advertencia de permisos | oci setup repair-file-permissions --file ~/.oci/config |
Missing option(s) --tenancy-id | Ese comando exige el tenancy explícito |
oci-cli api-key autenticacion
Red: compartment, VCN, subred y firewall
Qué se crea
| Recurso | Para qué |
|---|---|
| Compartment | Aísla el proyecto del resto de la cuenta; borrarlo limpia todo |
VCN 10.0.0.0/16 | Red privada virtual |
| Internet Gateway | Salida y entrada a internet |
Ruta 0.0.0.0/0 → gateway | En la tabla de rutas por defecto de la VCN |
| Security List | Entrada solo por 22 (SSH), 80 (HTTP) y 443 (HTTPS) + ICMP de fragmentación |
Subred pública 10.0.0.0/24 | Donde 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
--forceen losupdateevita la confirmación interactiva; esosupdatereemplazan la lista completa de reglas.- Protocolo
6= TCP,1= ICMP. La regla ICMP tipo 3 código 4 permite Path MTU Discovery. --wait-for-statehace que cada paso espere a que el recurso exista.- Para abrir otro puerto, agrega una regla al JSON
INGy vuelve a ejecutar elsecurity-list update.
Las dos capas de firewall
En Oracle hay dos firewalls y ambos deben permitir el puerto:
- Security List de la VCN (este capítulo).
iptablesdentro de la VM: la imagen Ubuntu de Oracle trae unREJECTpara 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 gratis | Hasta 2 OCPU / 12 GB | 1/8 OCPU / 1 GB |
| Disponibilidad | Suele dar “Out of host capacity” | Normalmente disponible |
| Regla de reclamo | CPU, red y memoria | Solo CPU y red |
| Evitar el reclamo | Instancia chica: la RAM usada ya supera el 20 % | Necesita un keepalive de CPU |
| Recomendación | Primera opción | Plan 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(consudosin 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
| Estrategia | Aplica a | Cómo | Evaluación |
|---|---|---|---|
| Instancia chica | A1 | 1 OCPU y 1 GB: el uso normal (~400 MB) supera el 20 % de RAM | Limpia, sin carga artificial. La mejor |
| Keepalive de CPU | Micro (y A1 si se quiere) | Cron que usa CPU poco más de un minuto cada pocos minutos | Funciona; es carga artificial (zona gris frente a los términos) |
| Tráfico de red | Ambas | Generar tráfico sostenido | Desperdicia 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
| Parte | Por qué |
|---|---|
*/10 | Cada 10 minutos |
timeout 75 | 75 s de carga ≈ 12,5 % del tiempo, más que el 5 % que necesita el p95; nunca queda corriendo |
nice -n 19 | Prioridad mínima: tus servicios siempre tienen la CPU primero |
sha256sum de /dev/urandom | Usa 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
| Fuente | Dónde corre | Para qué |
|---|---|---|
| Oracle Monitoring | API de Oracle, desde tu PC con oci | La cifra que ve Oracle |
| Registro local | La VM, cada minuto | Que 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 esidle.%en cron se escapa como\%.- Timer de systemd y no cron:
*/45en cron dispara a :00 y :45 (huecos de 15 min).OnUnitActiveSec=45mincuenta desde la última ejecución yOnBootSec=3minenví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-upgradesinstala 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-keepalivey/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
| Problema | Causa | Solución |
|---|---|---|
| No aparece la región que quiero | No admite cuentas gratis o cambió la disponibilidad | Elegir otra de la lista; la región no se cambia después |
NotAuthenticated | Fingerprint, user o tenancy mal copiados en ~/.oci/config | Volver a copiar el Configuration file preview y verificar el fingerprint con openssl |
Missing option(s) --tenancy-id | iam region-subscription list exige el tenancy | --tenancy-id "$(awk -F= '/^tenancy=/{print $2}' ~/.oci/config)" |
| Advertencia de permisos del config | Archivos legibles por otros | oci setup repair-file-permissions --file ~/.oci/config |
oci: command not found tras instalar | PATH de uv no cargado | uv tool update-shell y abrir otra terminal |
Red e instancia
| Problema | Causa | Solución |
|---|---|---|
NotAuthorizedOrNotFound justo después de crear el compartment | Propagación | sleep 20 antes de crear la VCN |
| ”Out of host capacity” en A1 | Sin hosts ARM libres para la capa gratis | Reintentar en bucle cada 2 min o usar Micro |
Cupos de A1 enormes en oci limits | Son del crédito de prueba | Respetar 2 OCPU / 12 GB de Always Free |
SSH Connection refused | cloud-init aún arrancando | Esperar 1–2 min y reintentar |
| 80/443 no responden aunque la Security List los permite | iptables de la imagen con REJECT final | Insertar el ACCEPT antes del REJECT y netfilter-persistent save |
apt muere por falta de memoria en la Micro | 1 GB sin swap | Swap de 1 GB (capítulo 07) |
Reclamo y monitoreo
| Problema | Causa | Solución |
|---|---|---|
| Un reporte dice “CPU 0 %“ | Foto de un instante | Mirar el p95 de 7 días |
| En Micro la RAM alta no protege | La regla de memoria solo aplica a A1 | Keepalive de CPU |
| ”Cada 45 min” con cron dispara a :00 y :45 | */45 no es un intervalo real | Timer de systemd con OnUnitActiveSec=45min |
Una línea de cron con % se corta | % es especial en crontab | Escaparlo como \% |
| La VM caída no avisa | No puede reportar si está muerta | La ausencia de reporte por más de 45 min es la alarma |
| Instancia reclamada | p95 de CPU (y red; en A1 también memoria) bajo 20 % por 7 días | Recrearla con los scripts; IP nueva → actualizar DNS |
troubleshooting oracle-cloud