second first commit
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
# AWS CLI & SDK Setup
|
||||
|
||||
Floci accepts any non-empty credentials — no real AWS account is needed.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
The simplest approach for local development:
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
## AWS CLI Profile
|
||||
|
||||
Add a dedicated profile to `~/.aws/config` and `~/.aws/credentials`:
|
||||
|
||||
```ini title="~/.aws/config"
|
||||
[profile floci]
|
||||
region = us-east-1
|
||||
output = json
|
||||
```
|
||||
|
||||
```ini title="~/.aws/credentials"
|
||||
[floci]
|
||||
aws_access_key_id = test
|
||||
aws_secret_access_key = test
|
||||
```
|
||||
|
||||
Then use it with every command:
|
||||
|
||||
```bash
|
||||
aws s3 ls --profile floci --endpoint-url http://localhost:4566
|
||||
```
|
||||
|
||||
Or set it as the default for your shell session:
|
||||
|
||||
```bash
|
||||
export AWS_PROFILE=floci
|
||||
export AWS_ENDPOINT_URL=http://localhost:4566
|
||||
```
|
||||
|
||||
## SDK Configuration
|
||||
|
||||
### Java (AWS SDK v2)
|
||||
|
||||
```java
|
||||
// Reusable endpoint override
|
||||
URI endpoint = URI.create("http://localhost:4566");
|
||||
AwsCredentialsProvider creds = StaticCredentialsProvider.create(
|
||||
AwsBasicCredentials.create("test", "test"));
|
||||
Region region = Region.US_EAST_1;
|
||||
|
||||
// Build any client the same way
|
||||
DynamoDbClient dynamo = DynamoDbClient.builder()
|
||||
.endpointOverride(endpoint)
|
||||
.region(region)
|
||||
.credentialsProvider(creds)
|
||||
.build();
|
||||
|
||||
SqsClient sqs = SqsClient.builder()
|
||||
.endpointOverride(endpoint)
|
||||
.region(region)
|
||||
.credentialsProvider(creds)
|
||||
.build();
|
||||
```
|
||||
|
||||
### Python (boto3)
|
||||
|
||||
```python
|
||||
import boto3
|
||||
|
||||
def floci_client(service):
|
||||
return boto3.client(
|
||||
service,
|
||||
endpoint_url="http://localhost:4566",
|
||||
region_name="us-east-1",
|
||||
aws_access_key_id="test",
|
||||
aws_secret_access_key="test",
|
||||
)
|
||||
|
||||
s3 = floci_client("s3")
|
||||
sqs = floci_client("sqs")
|
||||
dynamo = floci_client("dynamodb")
|
||||
```
|
||||
|
||||
### Node.js / TypeScript
|
||||
|
||||
```typescript
|
||||
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
||||
import { SQSClient } from "@aws-sdk/client-sqs";
|
||||
|
||||
const config = {
|
||||
endpoint: "http://localhost:4566",
|
||||
region: "us-east-1",
|
||||
credentials: { accessKeyId: "test", secretAccessKey: "test" },
|
||||
};
|
||||
|
||||
const dynamo = new DynamoDBClient(config);
|
||||
const sqs = new SQSClient(config);
|
||||
```
|
||||
|
||||
!!! tip "S3 path-style URLs"
|
||||
When using S3 with the AWS SDK v3 (Node.js), add `forcePathStyle: true` to the config object. Floci serves S3 in path-style mode (`http://localhost:4566/bucket-name`).
|
||||
|
||||
### Go
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||
)
|
||||
|
||||
cfg, err := config.LoadDefaultConfig(context.TODO(),
|
||||
config.WithRegion("us-east-1"),
|
||||
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider("test", "test", "")),
|
||||
config.WithEndpointResolverWithOptions(
|
||||
aws.EndpointResolverWithOptionsFunc(
|
||||
func(service, region string, opts ...interface{}) (aws.Endpoint, error) {
|
||||
return aws.Endpoint{URL: "http://localhost:4566"}, nil
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
## Account ID
|
||||
|
||||
Floci uses account ID `000000000000` in all ARNs and queue URLs by default:
|
||||
|
||||
```
|
||||
arn:aws:sqs:us-east-1:000000000000:my-queue
|
||||
http://localhost:4566/000000000000/my-queue
|
||||
```
|
||||
|
||||
Change the default with `FLOCI_DEFAULT_ACCOUNT_ID`:
|
||||
|
||||
```bash
|
||||
FLOCI_DEFAULT_ACCOUNT_ID=123456789012
|
||||
```
|
||||
|
||||
**Multi-account isolation** is also supported: if your access key ID is exactly 12 digits, Floci uses it directly as the account ID and fully isolates that account's resources from all others. See [Multi-Account Isolation](../configuration/multi-account.md) for details.
|
||||
@@ -0,0 +1,85 @@
|
||||
# Installation
|
||||
|
||||
Floci can be run three ways: as a Docker image, as a pre-built native binary, or built from source.
|
||||
|
||||
## Docker (Recommended)
|
||||
|
||||
No installation required beyond Docker itself.
|
||||
|
||||
```bash
|
||||
docker pull floci/floci:latest
|
||||
```
|
||||
|
||||
### Requirements
|
||||
|
||||
- Docker 20.10+
|
||||
- `docker compose` v2+ (plugin syntax, not standalone `docker-compose`)
|
||||
|
||||
## Image Tags
|
||||
|
||||
Each tag combines a **variant** (what's inside) and a **channel** (how stable).
|
||||
|
||||
| | Standard | Baseline (ARM64 only) | Compat (+ AWS CLI + boto3) |
|
||||
|---|---|---|---|
|
||||
| **Release (latest)** | `latest` ✅ | `latest-baseline` | `latest-compat` |
|
||||
| **Release (pinned)** | `x.y.z` | `x.y.z-baseline` | `x.y.z-compat` |
|
||||
| **Nightly (floating)** | `nightly` | — | `nightly-compat` |
|
||||
| **Nightly (dated)** | `nightly-mmddyyyy` | — | `nightly-mmddyyyy-compat` |
|
||||
|
||||
For the full breakdown see [Docker Images](../configuration/docker-images.md).
|
||||
|
||||
## Choosing a tag
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
# Standard release — recommended for most use cases
|
||||
services:
|
||||
floci:
|
||||
image: floci/floci:latest
|
||||
ports:
|
||||
- "4566:4566"
|
||||
```
|
||||
|
||||
Use the compat image if your workflow requires the AWS CLI or boto3 available inside the container:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
services:
|
||||
floci:
|
||||
image: floci/floci:latest-compat
|
||||
ports:
|
||||
- "4566:4566"
|
||||
```
|
||||
|
||||
Standard and compat have identical startup time (~24 ms) and memory footprint (~13 MiB). On Raspberry Pi 4-class ARM64 CPUs that lack LSE, use `floci/floci:latest-baseline` (or a pinned `x.y.z-baseline` release).
|
||||
|
||||
## Build from Source
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Java 25+
|
||||
- Maven 3.9+
|
||||
- (Optional) GraalVM Mandrel for native compilation
|
||||
|
||||
### Clone and run
|
||||
|
||||
```bash
|
||||
git clone https://github.com/floci-io/floci.git
|
||||
cd floci
|
||||
mvn quarkus:dev # dev mode with hot reload on port 4566
|
||||
```
|
||||
|
||||
### Build a production JAR
|
||||
|
||||
```bash
|
||||
mvn clean package -DskipTests
|
||||
java -jar target/quarkus-app/quarkus-run.jar
|
||||
```
|
||||
|
||||
### Build a native executable
|
||||
|
||||
```bash
|
||||
mvn clean package -Pnative -DskipTests
|
||||
./target/floci-runner
|
||||
```
|
||||
|
||||
!!! note
|
||||
Native compilation requires GraalVM or Mandrel with the `native-image` tool on your PATH. Build time is typically 2–5 minutes.
|
||||
@@ -0,0 +1,256 @@
|
||||
# Migrate from LocalStack
|
||||
|
||||
Floci is a drop-in replacement for LocalStack Community. The wire protocol, port, credentials, and SDK configuration are identical, so most migrations require only an image swap. This page documents every change and provides a compatibility mode for projects that need a gentler transition.
|
||||
|
||||
## Compatibility mode
|
||||
|
||||
LocalStack environment variable translation is **on by default**. Floci automatically maps LocalStack variables to their Floci equivalents at startup, so you can keep your existing environment variables unchanged:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
services:
|
||||
floci:
|
||||
image: floci/floci:latest
|
||||
ports:
|
||||
- "4566:4566"
|
||||
environment:
|
||||
# These LocalStack vars are automatically translated — no extra config needed:
|
||||
PERSISTENCE: "1" # → FLOCI_STORAGE_MODE=persistent
|
||||
LOCALSTACK_HOST: floci # → FLOCI_HOSTNAME=floci
|
||||
LAMBDA_DOCKER_NETWORK: mynet # → FLOCI_SERVICES_LAMBDA_DOCKER_NETWORK=mynet
|
||||
LAMBDA_REMOVE_CONTAINERS: "1" # → FLOCI_SERVICES_LAMBDA_EPHEMERAL=true
|
||||
DEBUG: "1" # → QUARKUS_LOG_LEVEL=DEBUG
|
||||
```
|
||||
|
||||
Explicitly set Floci variables always win — the translation only fills in values that haven't been set. To disable the translation entirely, set `LOCALSTACK_PARITY=false`.
|
||||
|
||||
Unless parity is disabled, the startup log also ends with a LocalStack-style `Ready.` line (in addition to Floci's own banner), so tooling that watches the container log for LocalStack's readiness message — such as the default wait strategy of Testcontainers' `LocalStackContainer` — works without a custom wait.
|
||||
|
||||
## Step-by-step migration
|
||||
|
||||
### 1 — Change the image
|
||||
|
||||
Pick the variant that matches your needs:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
# Before
|
||||
image: localstack/localstack
|
||||
|
||||
# After — no init scripts, or init scripts that don't call aws / boto3
|
||||
image: floci/floci:latest
|
||||
|
||||
# After — init scripts that use aws CLI or boto3 (AWS CLI + Python 3 + boto3 pre-installed)
|
||||
image: floci/floci:latest-compat
|
||||
```
|
||||
|
||||
To pin a specific release, replace `latest` / `latest-compat` with a version tag:
|
||||
|
||||
```yaml
|
||||
image: floci/floci:1.5.11
|
||||
image: floci/floci:1.5.11-compat
|
||||
```
|
||||
|
||||
The port (`4566`), credentials (`test` / `test`), and AWS SDK configuration are unchanged.
|
||||
|
||||
### 2 — Map environment variables
|
||||
|
||||
| LocalStack variable | Floci equivalent | Notes |
|
||||
|---|---|---|
|
||||
| `LOCALSTACK_HOST` | `FLOCI_HOSTNAME` | Hostname embedded in response URLs |
|
||||
| `LOCALSTACK_HOSTNAME` | `FLOCI_HOSTNAME` | Alias — same effect |
|
||||
| `PERSISTENCE=1` | `FLOCI_STORAGE_MODE=persistent` | Enable disk persistence |
|
||||
| `PERSIST_STATE=1` | `FLOCI_STORAGE_MODE=persistent` | Alias for `PERSISTENCE` — same effect |
|
||||
| `EDGE_PORT` | `FLOCI_PORT` | Bind port override |
|
||||
| `GATEWAY_LISTEN` | `QUARKUS_HTTP_HOST` | Bind address override. Also sets `FLOCI_SECURITY_ALLOW_UNSAFE_NETWORK_EXPOSURE=true`, since LocalStack needs no opt-in for a non-loopback address |
|
||||
| `LS_LOG` / `DEBUG=1` | `QUARKUS_LOG_LEVEL` | Log verbosity |
|
||||
| `DOCKER_HOST` | `FLOCI_DOCKER_DOCKER_HOST` | Docker daemon socket path or TCP address |
|
||||
| `LAMBDA_DOCKER_NETWORK` | `FLOCI_SERVICES_LAMBDA_DOCKER_NETWORK` | Network for Lambda containers |
|
||||
| `DOCKER_NETWORK` | `FLOCI_SERVICES_DOCKER_NETWORK` | Network for all spawned containers |
|
||||
| `LAMBDA_REMOVE_CONTAINERS=1` | `FLOCI_SERVICES_LAMBDA_EPHEMERAL=true` | Remove Lambda containers after invocation |
|
||||
| `USE_SSL=1` | `FLOCI_TLS_ENABLED=true` | Enable TLS/HTTPS — see [TLS / HTTPS](../configuration/tls.md) |
|
||||
| `CUSTOM_SSL_CERT_PATH` | `FLOCI_TLS_CERT_PATH` + `FLOCI_TLS_KEY_PATH` | LocalStack accepts a single combined PEM; Floci accepts it in both fields |
|
||||
| `SERVICES` | _(not needed)_ | Floci starts every service instantly; no selection required |
|
||||
| `LAMBDA_EXECUTOR` | _(not needed)_ | Floci always runs Lambda in Docker containers |
|
||||
| `LAMBDA_REMOTE_DOCKER` | _(not supported)_ | Use per-function `S3Bucket=hot-reload` instead — see [Lambda](../services/lambda.md) |
|
||||
|
||||
### 3 — Init scripts (no change required)
|
||||
|
||||
LocalStack init scripts mounted under `/etc/localstack/init/` run unchanged in Floci:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
volumes:
|
||||
- ./init/ready.d:/etc/localstack/init/ready.d:ro # works as-is
|
||||
```
|
||||
|
||||
Floci reads both `/etc/localstack/init/` (compat) and `/etc/floci/init/` (native). When the same filename exists in both, the Floci copy takes priority.
|
||||
|
||||
To use native Floci paths going forward:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
volumes:
|
||||
- ./init/ready.d:/etc/floci/init/ready.d:ro
|
||||
```
|
||||
|
||||
See [Initialization Hooks](../configuration/initialization-hooks.md) for the full four-phase lifecycle (`boot`, `start`, `ready`, `stop`) and script type details (`.sh`, `.py`).
|
||||
|
||||
### 4 — Init script tooling (compat image)
|
||||
|
||||
If your init scripts call `aws` or `boto3`, switch from `localstack/localstack` to `floci/floci:latest-compat`:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
# Before
|
||||
image: localstack/localstack
|
||||
|
||||
# After (includes Python 3, AWS CLI, boto3 — pre-configured for localhost:4566)
|
||||
image: floci/floci:latest-compat
|
||||
```
|
||||
|
||||
The compat image pre-configures the AWS CLI to talk to `http://localhost:4566` — no `--endpoint-url` flag is needed in scripts:
|
||||
|
||||
```sh
|
||||
#!/bin/sh
|
||||
aws sqs create-queue --queue-name orders # no --endpoint-url needed
|
||||
aws s3 mb s3://assets
|
||||
```
|
||||
|
||||
### 5 — Health and status endpoints
|
||||
|
||||
Floci serves the LocalStack-compatible status endpoint at both paths:
|
||||
|
||||
```
|
||||
GET /_localstack/init # LocalStack compat path — still works
|
||||
GET /_floci/init # native path
|
||||
```
|
||||
|
||||
If you wait on `/_localstack/init` or `/_localstack/health` in CI or scripts, no change is needed.
|
||||
|
||||
### 6 — Inspection endpoints
|
||||
|
||||
| Endpoint | Notes |
|
||||
|---|---|
|
||||
| `GET /_aws/ses` | Captured emails — identical |
|
||||
| `GET /_aws/ses?id=<id>` | Single message — identical |
|
||||
| `DELETE /_aws/ses` | Clear mailbox — identical |
|
||||
| `GET /_aws/sqs/messages?QueueUrl=<url>` | Non-destructive queue peek — identical |
|
||||
| `DELETE /_aws/sqs/messages?QueueUrl=<url>` | Purge queue — identical |
|
||||
|
||||
### 7 — Testcontainers
|
||||
|
||||
=== "Java"
|
||||
|
||||
Replace the `@LocalStackContainer` module with the Floci module:
|
||||
|
||||
```xml title="pom.xml"
|
||||
<!-- Before -->
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>localstack</artifactId>
|
||||
<version>1.21.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- After -->
|
||||
<dependency>
|
||||
<groupId>io.floci</groupId>
|
||||
<artifactId>testcontainers-floci</artifactId>
|
||||
<version>1.14.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
See the [Java Testcontainers guide](../testcontainers/java.md) for full setup.
|
||||
|
||||
=== "Python"
|
||||
|
||||
See the [Python Testcontainers guide](../testcontainers/python.md).
|
||||
|
||||
=== "Node.js"
|
||||
|
||||
See the [Node.js Testcontainers guide](../testcontainers/nodejs.md).
|
||||
|
||||
=== "Go"
|
||||
|
||||
See the [Go Testcontainers guide](../testcontainers/go.md).
|
||||
|
||||
## Complete before / after example
|
||||
|
||||
```yaml title="docker-compose.yml (before — LocalStack)"
|
||||
services:
|
||||
localstack:
|
||||
image: localstack/localstack
|
||||
ports:
|
||||
- "4566:4566"
|
||||
environment:
|
||||
LOCALSTACK_HOST: localstack
|
||||
PERSISTENCE: "1"
|
||||
LAMBDA_DOCKER_NETWORK: myapp_default
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./data:/var/lib/localstack
|
||||
- ./init/ready.d:/etc/localstack/init/ready.d:ro
|
||||
```
|
||||
|
||||
```yaml title="docker-compose.yml (after — Floci, minimal change)"
|
||||
services:
|
||||
floci:
|
||||
image: floci/floci:latest-compat # (1)
|
||||
ports:
|
||||
- "4566:4566"
|
||||
environment:
|
||||
LOCALSTACK_HOST: floci # translated automatically — no rename needed
|
||||
PERSISTENCE: "1"
|
||||
LAMBDA_DOCKER_NETWORK: myapp_default
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./data:/app/data # (2)
|
||||
- ./init/ready.d:/etc/localstack/init/ready.d:ro # compat path — unchanged
|
||||
```
|
||||
|
||||
1. Switch to `latest-compat` if your init scripts use `aws` or `boto3`.
|
||||
2. LocalStack stores data in `/var/lib/localstack`; Floci uses `/app/data`.
|
||||
|
||||
## S3 virtual-hosted style DNS
|
||||
|
||||
If you use LocalStack's public wildcard DNS (`*.s3.localhost.localstack.cloud`) for S3 virtual-hosted style addressing, Floci supports it without any change:
|
||||
|
||||
```java
|
||||
// This LocalStack endpoint works unchanged with Floci
|
||||
S3Client s3 = S3Client.builder()
|
||||
.endpointOverride(URI.create("http://s3.localhost.localstack.cloud:4566"))
|
||||
.build();
|
||||
// SDK sends to: my-bucket.s3.localhost.localstack.cloud:4566 → Floci
|
||||
```
|
||||
|
||||
Floci also registers its own wildcard DNS domains for virtual-hosted style:
|
||||
|
||||
| Domain | Usage |
|
||||
|---|---|
|
||||
| `*.s3.localhost.floci.io` | S3 virtual-hosted style (`bucket.s3.localhost.floci.io`) |
|
||||
| `*.localhost.floci.io` | Direct subdomain style (`bucket.localhost.floci.io`) |
|
||||
|
||||
DNS resolution works differently depending on where the client runs:
|
||||
|
||||
**From the host machine** — both `*.localhost.localstack.cloud` and `*.localhost.floci.io` are registered in public DNS and resolve to `127.0.0.1`. Requests reach Floci via the Docker port binding (`4566:4566`) with no extra configuration.
|
||||
|
||||
**From inside a Docker container** — `127.0.0.1` is the container's own loopback, not Floci. Floci's embedded DNS server handles this: it resolves `*.localhost.floci.io` and `*.localhost.localstack.cloud` (and `*.localhost.localstack.cloud` subdomains) to Floci's container IP on the Docker network. Spawned containers (Lambda, RDS, ElastiCache) are automatically configured to use Floci as their DNS resolver, so virtual-hosted S3 URLs work inside them without any extra setup.
|
||||
|
||||
See [S3 → Virtual-Hosted Style](../services/s3.md#virtual-hosted-style) for full details and SDK examples.
|
||||
|
||||
## What stays the same
|
||||
|
||||
- Port `4566`
|
||||
- All AWS SDK and CLI calls — no code changes
|
||||
- Dummy credentials (`test` / `test`)
|
||||
- Init scripts under `/etc/localstack/init/` (compat paths)
|
||||
- `/_localstack/init` and `/_localstack/health` endpoints
|
||||
- `/_aws/ses` and `/_aws/sqs/messages` inspection endpoints
|
||||
- Docker socket mount for Lambda, RDS, and ElastiCache
|
||||
|
||||
## Known differences
|
||||
|
||||
| Area | LocalStack | Floci |
|
||||
|---|---|---|
|
||||
| Lambda executor | Configurable (`LAMBDA_EXECUTOR`) | Always Docker containers |
|
||||
| `LAMBDA_REMOTE_DOCKER` | Supported | Not supported — use per-function `S3Bucket=hot-reload` instead |
|
||||
| Service selection | `SERVICES=sqs,s3,...` | All services start automatically; no selection |
|
||||
| Data directory | `/var/lib/localstack` | `/app/data` |
|
||||
| Log variable | `LS_LOG` / `DEBUG` | `QUARKUS_LOG_LEVEL` |
|
||||
@@ -0,0 +1,213 @@
|
||||
# Quick Start
|
||||
|
||||
This guide gets Floci running and verifies that AWS CLI commands work against it in under five minutes.
|
||||
|
||||
## Step 1 — Start Floci
|
||||
|
||||
=== "Native (recommended)"
|
||||
|
||||
`latest` is the native image — sub-second startup, minimal memory:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
floci:
|
||||
image: floci/floci:latest
|
||||
ports:
|
||||
- "4566:4566"
|
||||
volumes:
|
||||
# Local directory bind mount (default)
|
||||
- ./data:/app/data
|
||||
|
||||
# OR named volume (optional):
|
||||
# - floci-data:/app/data
|
||||
|
||||
# volumes:
|
||||
# floci-data:
|
||||
```
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
=== "Compat (+ AWS CLI + boto3)"
|
||||
|
||||
Use `latest-compat` if you need the AWS CLI and boto3 inside the container:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
floci:
|
||||
image: floci/floci:latest-compat
|
||||
ports:
|
||||
- "4566:4566"
|
||||
volumes:
|
||||
# Local directory bind mount (default)
|
||||
- ./data:/app/data
|
||||
|
||||
# OR named volume (optional):
|
||||
# - floci-data:/app/data
|
||||
|
||||
# volumes:
|
||||
# floci-data:
|
||||
```
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
=== "Build from source"
|
||||
|
||||
```bash
|
||||
git clone https://github.com/floci-io/floci.git
|
||||
cd floci
|
||||
mvn quarkus:dev # hot reload, port 4566
|
||||
```
|
||||
|
||||
## Step 2 — Configure AWS CLI
|
||||
|
||||
Floci accepts any dummy credentials — no real AWS account needed.
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
Add these to your shell profile (`.bashrc` / `.zshrc`) so they persist across sessions.
|
||||
|
||||
## Step 3 — Verify the Setup
|
||||
|
||||
Run a few quick smoke tests:
|
||||
|
||||
```bash
|
||||
# S3 — create a bucket and upload a file
|
||||
aws s3 mb s3://my-bucket --endpoint-url $AWS_ENDPOINT_URL
|
||||
echo "hello floci" | aws s3 cp - s3://my-bucket/hello.txt --endpoint-url $AWS_ENDPOINT_URL
|
||||
aws s3 ls s3://my-bucket --endpoint-url $AWS_ENDPOINT_URL
|
||||
|
||||
# SQS — create a queue and send a message
|
||||
aws sqs create-queue --queue-name orders --endpoint-url $AWS_ENDPOINT_URL
|
||||
aws sqs send-message \
|
||||
--queue-url $AWS_ENDPOINT_URL/000000000000/orders \
|
||||
--message-body '{"event":"order.placed"}' \
|
||||
--endpoint-url $AWS_ENDPOINT_URL
|
||||
|
||||
# DynamoDB — create a table
|
||||
aws dynamodb create-table \
|
||||
--table-name Users \
|
||||
--attribute-definitions AttributeName=id,AttributeType=S \
|
||||
--key-schema AttributeName=id,KeyType=HASH \
|
||||
--billing-mode PAY_PER_REQUEST \
|
||||
--endpoint-url $AWS_ENDPOINT_URL
|
||||
```
|
||||
|
||||
You should see successful responses for all three commands.
|
||||
|
||||
## Step 4 — Use in Your Application
|
||||
|
||||
Point your AWS SDK to Floci the same way:
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java
|
||||
S3Client s3 = S3Client.builder()
|
||||
.endpointOverride(URI.create("http://localhost:4566"))
|
||||
.region(Region.US_EAST_1)
|
||||
.credentialsProvider(StaticCredentialsProvider.create(
|
||||
AwsBasicCredentials.create("test", "test")))
|
||||
.build();
|
||||
```
|
||||
|
||||
=== "Python (boto3)"
|
||||
|
||||
```python
|
||||
import boto3
|
||||
|
||||
s3 = boto3.client(
|
||||
"s3",
|
||||
endpoint_url="http://localhost:4566",
|
||||
region_name="us-east-1",
|
||||
aws_access_key_id="test",
|
||||
aws_secret_access_key="test",
|
||||
)
|
||||
```
|
||||
|
||||
=== "Node.js"
|
||||
|
||||
```javascript
|
||||
import { S3Client } from "@aws-sdk/client-s3";
|
||||
|
||||
const s3 = new S3Client({
|
||||
endpoint: "http://localhost:4566",
|
||||
region: "us-east-1",
|
||||
credentials: { accessKeyId: "test", secretAccessKey: "test" },
|
||||
forcePathStyle: true,
|
||||
});
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go
|
||||
cfg, _ := config.LoadDefaultConfig(context.TODO(),
|
||||
config.WithRegion("us-east-1"),
|
||||
config.WithEndpointResolverWithOptions(
|
||||
aws.EndpointResolverWithOptionsFunc(func(service, region string, opts ...interface{}) (aws.Endpoint, error) {
|
||||
return aws.Endpoint{URL: "http://localhost:4566"}, nil
|
||||
}),
|
||||
),
|
||||
)
|
||||
client := s3.NewFromConfig(cfg)
|
||||
```
|
||||
|
||||
## Step 5 — (Optional) Push and pull a container image to emulated ECR
|
||||
|
||||
Floci emulates ECR with a real OCI registry behind it, so the stock `docker` client works against repositories you create through the AWS CLI. No daemon configuration needed — Floci returns repository URIs that resolve to loopback, which `docker` auto-trusts as insecure.
|
||||
|
||||
```bash
|
||||
# Create the repository (lazy-starts the backing registry container)
|
||||
aws ecr create-repository --repository-name floci-it/app --endpoint-url $AWS_ENDPOINT_URL
|
||||
|
||||
# Authenticate
|
||||
aws ecr get-login-password --endpoint-url $AWS_ENDPOINT_URL \
|
||||
| docker login --username AWS --password-stdin \
|
||||
000000000000.dkr.ecr.us-east-1.localhost:4566
|
||||
|
||||
# Push
|
||||
docker pull alpine:3.19
|
||||
docker tag alpine:3.19 000000000000.dkr.ecr.us-east-1.localhost:4566/floci-it/app:v1
|
||||
docker push 000000000000.dkr.ecr.us-east-1.localhost:4566/floci-it/app:v1
|
||||
|
||||
# Pull from a clean local image store
|
||||
docker rmi 000000000000.dkr.ecr.us-east-1.localhost:4566/floci-it/app:v1
|
||||
docker pull 000000000000.dkr.ecr.us-east-1.localhost:4566/floci-it/app:v1
|
||||
```
|
||||
|
||||
See the [ECR service docs](../services/ecr.md) for the full action surface, image-backed Lambda integration, and CDK `DockerImageFunction` support.
|
||||
|
||||
## Lambda on native Linux Docker (UFW)
|
||||
|
||||
When Floci runs **natively on a Linux host** (not Docker Desktop), Lambda function containers reach Floci's Runtime API server via the docker bridge gateway. On Ubuntu / Pop!_OS / Debian boxes with **UFW enabled**, the default `INPUT DROP` policy silently drops these packets and Lambda invocations time out with `Function.TimedOut`. This affects every Lambda packaging type — Zip *and* image-backed functions deployed via emulated ECR.
|
||||
|
||||
**One-time fix**, scoped to the docker bridge only (does not expose anything to the network — `docker0` is internal):
|
||||
|
||||
```bash
|
||||
sudo ufw allow in on docker0 comment 'floci: containers reach host'
|
||||
```
|
||||
|
||||
If you want to scope it tighter to the Lambda Runtime API and Floci's ECR data plane:
|
||||
|
||||
```bash
|
||||
sudo ufw allow in on docker0 to any port 12000:12499 proto tcp comment 'floci lambda runtime api'
|
||||
sudo ufw allow in on docker0 to any port 4566 proto tcp comment 'floci ecr data plane'
|
||||
```
|
||||
|
||||
**Docker Desktop** (macOS / Windows / Linux) does not need this — it routes container → host through the Docker VM, which Floci's `DockerHostResolver` detects automatically.
|
||||
|
||||
**Floci-in-Docker** (running the published Floci image inside a container) does not need this either — Lambda containers and Floci share the same docker network and reach each other via container IPs.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Configure Docker Compose with ElastiCache and RDS ports](../configuration/docker-compose.md)
|
||||
- [Environment variables reference](../configuration/environment-variables.md)
|
||||
- [application.yml reference (source builds)](../configuration/advanced/application-yml.md)
|
||||
- [Browse per-service documentation](../services/index.md)
|
||||
@@ -0,0 +1,144 @@
|
||||
# Terraform with Floci
|
||||
|
||||
Terraform can use Floci as a local AWS-compatible endpoint. Terraform sends requests through the standard `hashicorp/aws` provider, while Floci stores and emulates the resources locally. No real AWS account or cloud resources are used.
|
||||
|
||||
## Start Floci
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
services:
|
||||
floci:
|
||||
image: floci/floci:latest
|
||||
ports:
|
||||
- "4566:4566"
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
```
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Configure the AWS provider
|
||||
|
||||
Create `provider.tf`:
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
access_key = "test"
|
||||
secret_key = "test"
|
||||
|
||||
skip_credentials_validation = true
|
||||
skip_metadata_api_check = true
|
||||
skip_requesting_account_id = true
|
||||
s3_use_path_style = true
|
||||
|
||||
endpoints {
|
||||
dynamodb = "http://localhost:4566"
|
||||
secretsmanager = "http://localhost:4566"
|
||||
s3 = "http://localhost:4566"
|
||||
sns = "http://localhost:4566"
|
||||
sqs = "http://localhost:4566"
|
||||
ssm = "http://localhost:4566"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The endpoint must be configured for every AWS service used by the Terraform configuration. See the [services overview](../services/index.md) for the services implemented by Floci.
|
||||
|
||||
## Create local AWS resources
|
||||
|
||||
Create `main.tf`:
|
||||
|
||||
```hcl
|
||||
resource "aws_s3_bucket" "app" {
|
||||
bucket = "floci-terraform-example"
|
||||
}
|
||||
|
||||
resource "aws_sqs_queue" "jobs" {
|
||||
name = "floci-terraform-jobs"
|
||||
}
|
||||
|
||||
resource "aws_dynamodb_table" "items" {
|
||||
name = "floci-terraform-items"
|
||||
billing_mode = "PAY_PER_REQUEST"
|
||||
hash_key = "id"
|
||||
|
||||
attribute {
|
||||
name = "id"
|
||||
type = "S"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_ssm_parameter" "environment" {
|
||||
name = "/floci/environment"
|
||||
type = "String"
|
||||
value = "local"
|
||||
}
|
||||
```
|
||||
|
||||
Initialize and apply the configuration:
|
||||
|
||||
```bash
|
||||
terraform init
|
||||
terraform validate
|
||||
terraform plan
|
||||
terraform apply
|
||||
```
|
||||
|
||||
Verify a resource through the AWS CLI:
|
||||
|
||||
```bash
|
||||
aws --endpoint-url http://localhost:4566 \
|
||||
s3api head-bucket --bucket floci-terraform-example
|
||||
```
|
||||
|
||||
Remove the local resources when finished:
|
||||
|
||||
```bash
|
||||
terraform destroy
|
||||
```
|
||||
|
||||
## Terraform state in Floci
|
||||
|
||||
Terraform can keep state locally by using its default local backend. For a fully emulated AWS workflow, Floci also supports the S3 backend and DynamoDB locking used by the compatibility suite:
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
backend "s3" {
|
||||
bucket = "tfstate"
|
||||
key = "terraform.tfstate"
|
||||
region = "us-east-1"
|
||||
endpoint = "http://localhost:4566"
|
||||
dynamodb_endpoint = "http://localhost:4566"
|
||||
dynamodb_table = "tflock"
|
||||
access_key = "test"
|
||||
secret_key = "test"
|
||||
skip_credentials_validation = true
|
||||
skip_region_validation = true
|
||||
use_path_style = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Create the backend resources before running `terraform init`:
|
||||
|
||||
```bash
|
||||
aws --endpoint-url http://localhost:4566 s3api create-bucket --bucket tfstate
|
||||
aws --endpoint-url http://localhost:4566 dynamodb create-table \
|
||||
--table-name tflock \
|
||||
--attribute-definitions AttributeName=LockID,AttributeType=S \
|
||||
--key-schema AttributeName=LockID,KeyType=HASH \
|
||||
--billing-mode PAY_PER_REQUEST
|
||||
```
|
||||
|
||||
For a complete automated example covering more services and the full lifecycle, see [`compatibility-tests/compat-terraform`](https://github.com/floci-io/floci/tree/main/compatibility-tests/compat-terraform).
|
||||
Reference in New Issue
Block a user