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:
- Costo de llamadas de desarrollo — cada test que toca S3, SQS o DynamoDB consume créditos.
- Dependencia de red — sin internet o con latencia alta, el ciclo de desarrollo se rompe.
- 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:
| Floci | LocalStack Community | |
|---|---|---|
| Token requerido | No | Sí (desde mar 2026) |
| Startup | ~24 ms | ~3.3 s |
| Memoria idle | ~13 MiB | ~143 MiB |
| Imagen Docker | ~90 MB | ~1.0 GB |
| Actualizaciones de seguridad | Sí | Congeladas |
| Licencia | MIT | Restringida |
| Servicios | 47 | Subconjunto |
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
- Docker Desktop (o Docker Engine + Docker Compose en Linux)
- AWS CLI instalado (
aws --version) - Para ECS o Lambda: acceso al Docker socket (se configura abajo)
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:
| Modo | Comportamiento | Cuándo usarlo |
|---|---|---|
memory | Solo RAM, datos se pierden al reiniciar | CI/CD, tests unitarios |
hybrid | RAM + flush cada 5s | Desarrollo local con estado |
persistent | Flush en shutdown graceful | Estado importante que puede perderse brevemente |
wal | Write-Ahead Log — máxima durabilidad | Estado 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: truees 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:
- Blob Storage — equivalente a S3
- Queue Storage — equivalente a SQS
- Table Storage — NoSQL clave-valor
- Azure Functions — ejecución real vía Docker-in-Docker
- App Configuration — feature flags y parámetros
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:
- Variables de entorno con prefijo
LOCALSTACK_*→ usarFLOCI_*equivalentes - La variable
LOCALSTACK_AUTH_TOKENya no existe ni se necesita - El endpoint de health cambió a
/_floci/health(antes/_localstack/health)
Referencias
- Floci (AWS): github.com/floci-io/floci
- Floci-az (Azure): github.com/floci-io/floci-az
- Docker Hub Floci:
floci/floci:latest - Docker Hub Floci-az:
floci/floci-az:latest - Comunidad Slack: floci.slack.com