awsazuredockerlocal-devflocilocalstacktestingcloud 12 min de lectura

LocalStack murio. Floci es el reemplazo gratuito para emular AWS y Azure en local

LocalStack elimino su edicion gratuita en marzo 2026. Floci cubre AWS con 47 servicios sin token y sin costo. Floci-az hace lo mismo para Azure. Tutorial completo de instalacion e integracion.

Si usabas LocalStack para emular servicios AWS en local, en marzo 2026 se acabó la fiesta. LocalStack anunció el sunset de su edición community: ahora requiere token de autenticación, congela las actualizaciones de seguridad y recorta servicios disponibles.

La alternativa se llama Floci. MIT, sin token, 24 ms de startup, 13 MiB en idle. 47 servicios AWS. Y para Azure existe Floci-az, su hermano directo.

Este tutorial cubre instalación, configuración, prueba de servicios e integración con tu app.


Por qué importa tener un emulador local

Cuando desarrollas contra servicios cloud reales tienes tres problemas:

  1. Costo de llamadas de desarrollo — cada test que toca S3, SQS o DynamoDB consume créditos.
  2. Dependencia de red — sin internet o con latencia alta, el ciclo de desarrollo se rompe.
  3. Estado compartido — varios devs usando el mismo bucket de dev generan colisiones de datos.

Un emulador local elimina las tres. Corres todo en localhost, los tests son deterministas, y no hay factura sorpresa al final del mes.


Qué es Floci

Floci es un emulador de AWS que corre como contenedor Docker. Expone la misma API wire que AWS en http://localhost:4566, así que tu AWS CLI, SDK de Python (boto3), SDK de Node.js (@aws-sdk/*) o cualquier otra herramienta funciona sin cambiar código — solo cambias el endpoint.

Diferencias clave con LocalStack:

FlociLocalStack Community
Token requeridoNoSí (desde mar 2026)
Startup~24 ms~3.3 s
Memoria idle~13 MiB~143 MiB
Imagen Docker~90 MB~1.0 GB
Actualizaciones de seguridadCongeladas
LicenciaMITRestringida
Servicios47Subconjunto

Servicios disponibles incluyen S3, SQS, SNS, DynamoDB, RDS, ElastiCache (Redis con IAM auth), Cognito, ECS, EC2, Lambda, Kinesis, MSK (Kafka), Athena, IAM, STS, Secrets Manager, CodeBuild, CodeDeploy, y más.


Requisitos


Instalación básica

Crea un docker-compose.yml en tu proyecto:

services:
  floci:
    image: floci/floci:latest
    container_name: floci-local
    ports:
      - "4566:4566"
    environment:
      FLOCI_DEFAULT_REGION: us-east-1
      FLOCI_STORAGE_MODE: memory
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:4566/_floci/health"]
      interval: 10s
      timeout: 5s
      retries: 3

Levanta el contenedor:

docker compose up -d

Verifica que está corriendo:

curl http://localhost:4566/_floci/health
# {"status":"ok","version":"..."}

Configurar el AWS CLI para Floci

Floci no valida credenciales en modo desarrollo. Puedes usar cualquier valor:

export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_DEFAULT_REGION=us-east-1
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test

O configura un perfil dedicado en ~/.aws/config:

[profile floci]
region = us-east-1
endpoint_url = http://localhost:4566
# ~/.aws/credentials
[floci]
aws_access_key_id = test
aws_secret_access_key = test

Usa el perfil con --profile floci o AWS_PROFILE=floci.


Probar los servicios principales

S3

# Crear bucket
aws s3 mb s3://mi-bucket

# Subir archivo
echo "hola mundo" > test.txt
aws s3 cp test.txt s3://mi-bucket/

# Listar contenido
aws s3 ls s3://mi-bucket/

# Descargar
aws s3 cp s3://mi-bucket/test.txt ./descargado.txt

SQS

# Crear cola
aws sqs create-queue --queue-name mi-cola

# Obtener URL
QUEUE_URL=$(aws sqs get-queue-url --queue-name mi-cola --query QueueUrl --output text)

# Enviar mensaje
aws sqs send-message --queue-url "$QUEUE_URL" --message-body "mensaje de prueba"

# Recibir mensaje
aws sqs receive-message --queue-url "$QUEUE_URL"

DynamoDB

# Crear tabla
aws dynamodb create-table \
  --table-name Usuarios \
  --attribute-definitions AttributeName=id,AttributeType=S \
  --key-schema AttributeName=id,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST

# Insertar item
aws dynamodb put-item \
  --table-name Usuarios \
  --item '{"id":{"S":"u001"},"nombre":{"S":"Ana"},"email":{"S":"ana@ejemplo.com"}}'

# Consultar
aws dynamodb get-item \
  --table-name Usuarios \
  --key '{"id":{"S":"u001"}}'

Secrets Manager

# Crear secret
aws secretsmanager create-secret \
  --name /mi-app/db \
  --secret-string '{"host":"localhost","password":"secret123"}'

# Leer secret
aws secretsmanager get-secret-value --secret-id /mi-app/db

Persistencia de datos

Por defecto Floci usa modo memory — los datos desaparecen al reiniciar el contenedor. Para desarrollo con estado persistente:

services:
  floci:
    image: floci/floci:latest
    environment:
      FLOCI_DEFAULT_REGION: us-east-1
      FLOCI_STORAGE_MODE: hybrid   # RAM + flush async cada 5s
    volumes:
      - ./floci-data:/app/data
    ports:
      - "4566:4566"

Modos disponibles:

ModoComportamientoCuándo usarlo
memorySolo RAM, datos se pierden al reiniciarCI/CD, tests unitarios
hybridRAM + flush cada 5sDesarrollo local con estado
persistentFlush en shutdown gracefulEstado importante que puede perderse brevemente
walWrite-Ahead Log — máxima durabilidadEstado crítico que no puede perderse

Integración con Python (boto3)

import boto3

# Cliente apuntando a Floci
s3 = boto3.client(
    "s3",
    endpoint_url="http://localhost:4566",
    region_name="us-east-1",
    aws_access_key_id="test",
    aws_secret_access_key="test",
)

# Crear bucket y subir archivo
s3.create_bucket(Bucket="mi-bucket")
s3.put_object(Bucket="mi-bucket", Key="datos.json", Body=b'{"ok": true}')

# Leer
response = s3.get_object(Bucket="mi-bucket", Key="datos.json")
print(response["Body"].read())  # b'{"ok": true}'

Para SQS:

sqs = boto3.client(
    "sqs",
    endpoint_url="http://localhost:4566",
    region_name="us-east-1",
    aws_access_key_id="test",
    aws_secret_access_key="test",
)

queue = sqs.create_queue(QueueName="mi-cola")
url = queue["QueueUrl"]

sqs.send_message(QueueUrl=url, MessageBody="evento-procesado")
msgs = sqs.receive_message(QueueUrl=url, MaxNumberOfMessages=1)
print(msgs["Messages"][0]["Body"])  # "evento-procesado"

Recomendación: usa variables de entorno para el endpoint_url y condiciona por entorno (local vs production):

import os
import boto3

endpoint = os.getenv("AWS_ENDPOINT_URL")  # None en producción, http://localhost:4566 en local

s3 = boto3.client(
    "s3",
    endpoint_url=endpoint,   # None = usa AWS real
    region_name=os.getenv("AWS_DEFAULT_REGION", "us-east-1"),
)

Integración con Node.js / TypeScript

import { S3Client, CreateBucketCommand, PutObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({
  region: process.env.AWS_DEFAULT_REGION ?? "us-east-1",
  endpoint: process.env.AWS_ENDPOINT_URL,  // undefined en prod, http://localhost:4566 en local
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? "test",
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? "test",
  },
  forcePathStyle: true,  // requerido para S3 local
});

await s3.send(new CreateBucketCommand({ Bucket: "mi-bucket" }));
await s3.send(new PutObjectCommand({
  Bucket: "mi-bucket",
  Key: "archivo.json",
  Body: Buffer.from(JSON.stringify({ ok: true })),
}));

Nota: forcePathStyle: true es necesario para S3 en emuladores locales. En producción con AWS real puedes omitirlo.


ECS y Lambda (requieren Docker socket)

Si necesitas emular ECS o Lambda, Floci lanza contenedores Docker reales. El docker-compose debe incluir el socket:

services:
  floci:
    image: floci/floci:latest
    ports:
      - "4566:4566"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./floci-data:/app/data
    environment:
      FLOCI_DEFAULT_REGION: us-east-1
      FLOCI_STORAGE_MODE: hybrid
    user: root   # requerido para acceder al Docker socket

Floci-az: emulador de Azure

Para proyectos con Azure, existe floci-az. Mismo concepto, misma filosofía. Corre en el puerto 4577 y emula:

services:
  floci-az:
    image: floci/floci-az:latest
    container_name: floci-azure-local
    ports:
      - "4577:4577"
    volumes:
      - ./floci-az-data:/app/data
      - /var/run/docker.sock:/var/run/docker.sock  # solo para Functions
    environment:
      FLOCI_AZ_STORAGE_MODE: hybrid

Connection string para tu app:

DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMh0==;BlobEndpoint=http://localhost:4577/devstoreaccount1;QueueEndpoint=http://localhost:4577/devstoreaccount1-queue;TableEndpoint=http://localhost:4577/devstoreaccount1-table;

Prueba rápida con Azure CLI:

az storage container create \
  --name mi-contenedor \
  --connection-string "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMh0==;BlobEndpoint=http://localhost:4577/devstoreaccount1;"

Setup completo: AWS + Azure en paralelo

Si tu proyecto usa ambas nubes (o quieres practicar las dos):

services:
  floci:
    image: floci/floci:latest
    container_name: floci-aws
    ports:
      - "4566:4566"
    environment:
      FLOCI_DEFAULT_REGION: us-east-1
      FLOCI_STORAGE_MODE: hybrid
    volumes:
      - ./data/aws:/app/data

  floci-az:
    image: floci/floci-az:latest
    container_name: floci-azure
    ports:
      - "4577:4577"
    environment:
      FLOCI_AZ_STORAGE_MODE: hybrid
    volumes:
      - ./data/azure:/app/data
      - /var/run/docker.sock:/var/run/docker.sock
docker compose up -d
# AWS disponible en :4566
# Azure disponible en :4577

Variables de entorno para tu proyecto

Crea un .env.local (nunca al repo):

# AWS → Floci local
AWS_ENDPOINT_URL=http://localhost:4566
AWS_DEFAULT_REGION=us-east-1
AWS_ACCESS_KEY_ID=test
AWS_SECRET_ACCESS_KEY=test

# Azure → Floci-az local
AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMh0==;BlobEndpoint=http://localhost:4577/devstoreaccount1;

En producción, estas variables simplemente no se setean (o usan valores reales), y los SDKs apuntan automáticamente a AWS/Azure reales.


Migrar desde LocalStack

Si ya usas LocalStack, la migración es directa. Floci expone el mismo endpoint en el mismo puerto:

# Antes (LocalStack)
image: localstack/localstack:latest
ports: "4566:4566"

# Después (Floci) — mismo puerto, mismo endpoint
image: floci/floci:latest
ports: "4566:4566"

Las únicas diferencias que pueden requerir ajuste:


Referencias

Ver más artículos Compartir →