> For the complete documentation index, see [llms.txt](https://islamu.gitbook.io/islamu-event/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://islamu.gitbook.io/islamu-event/documentation/readme/self-hosting/docker-standalone.md).

# Docker Standalone

Deploy and operate the single-container standalone distribution with durable SQLite storage.

The standalone image (`Event.Standalone`) is the simplest and lowest-overhead operational topology for ISLAMU Event. It packages the API, background workers, Blazor WebAssembly BFF/UI, health endpoints, and in-process database migrations into **a single non-root container process**.

You can complete Local administrator setup and use the core without SMTP, Mailpit or a credential email address. Public operator contact and legal identity are not required to finish setup; configure them before dependent disclosure, directory activation or paid commerce. Email delivery starts disabled and is an optional, administrator-controlled capability.

After setup and Local password replacement, sign in afresh and open `/settings/instance?section=getting-started`. A new SingleTenant directory stays Provisioning and private until explicitly activated. Setup status recovers a lost completion response without replaying credentials: select **Refresh status** and the page will use public terminal status only to show the Local sign-in handoff. It clears the password and setup session and never resubmits credentials. If setup is still pending, private setup status remains authoritative and setup authority is retained. Completed setup stays locked. Use the [categorized checklist](/islamu-event/documentation/readme/administration-and-branding/admin-guide.md#getting-started-after-setup) to distinguish disclosure and paid-event prerequisites from recommended operational work. Optional profile details are disclosed on demand; provider failures and restart requirements are not hidden by the simplified setup form. Completion also checks provider readiness on the server: a blocked authorization provider cannot be bypassed by submitting setup directly. Resolve the blocker and refresh setup before completing; rejected attempts do not create administrator credentials. If another setup tab saves the profile while completion is in flight, completion rejects the stale version instead of replacing that saved profile. Refresh before retrying. Profile saves already in flight cannot change the instance after setup has completed; setup authority remains permanently locked.

Authentication-provider, authorization-provider and instance operator-identity settings remain manageable with active setup authority or a signed-in instance administrator while the directory is private. MultiTenant instances do not need to create or bind an unrelated directory first. Ordinary users and tenant-only administrators do not gain instance-management access, and public pages stay unavailable until explicit directory activation.

***

## 1. When to Choose Standalone

| Advantage                                                                                                       | Consideration                                                                                                       |
| --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Zero External Infrastructure**: Runs on built-in SQLite persistence; no PostgreSQL server or Redis required.  | **Single Replica**: SQLite requires exactly one running container instance (no horizontal multi-container scaling). |
| **Single-Process Footprint**: Runs API, BFF/UI, and SQLite in one container without auxiliary database servers. | **Local-First Storage**: Media and database files live in a mounted Docker volume.                                  |
| **Instant Onboarding**: In-process migrations apply automatically before the HTTP port opens.                   | **Multi-Platform Support**: Official multi-arch images built for `linux/amd64` and `linux/arm64`.                   |

SQLite coordinates transaction-owned work inside the application process. Failed operations release that coordination when their transaction and connection are cleaned up, so later checkout or configuration requests are not left waiting on an abandoned lock. This does not change the one-replica requirement.

***

## 2. Quick Run & Production Deployment

### Step 1: Create a Persistent Volume

ISLAMU Event Standalone requires persistent storage mounted at `/app/data` to retain the primary database, privacy-erasure authority, Data Protection keys, and uploaded media:

Local upload bytes default to `/app/data/storage`, even without an explicit root setting. A deployment override uses `Storage__Local__RootPath` and still wins; mount and back up that directory too if it is outside `/app/data`. The `LOCAL_STORAGE_ROOT_PATH` alias is translated by Docker Compose, not by Standalone's direct `docker run --env-file` invocation.

```bash
docker volume create event_standalone_data
```

### Step 2: Prepare Configuration (`.env`)

Create a private `.env` file containing your production settings. The repository `.env.example` is a curated baseline, not a ready-to-run secret file or an exhaustive reference. Its PostgreSQL host, port and role settings must not be carried into SQLite configuration. Use the small Standalone projection below and the [environment reference](/islamu-event/documentation/readme/configuration-and-operations/environment-variables.md) for advanced settings. Restrict the file to its operator (`chmod 600 .env`).

> \[!TIP] **Authentication recommendation:** Keep the default embedded Local Identity for the easiest standalone deployment and for localhost/private-network use. Choose AT Protocol second when the instance has a public HTTPS domain and you want Bluesky/AT Protocol accounts to handle password authentication instead of storing user passwords yourself. AT Protocol is not the default because its OAuth callback cannot operate on localhost. Choose Keycloak for professional or SaaS operations that need advanced SSO/federation, 2FA/MFA, and centralized identity administration.

```env
ASPNETCORE_ENVIRONMENT=Production
SECRET_PROVIDER=Environment
DATABASE_PROVIDER=sqlite
DEPLOYMENT_MODE=SingleTenant
Storage__Local__RootPath=/app/data/storage

# Bounded SQLite profile: optional processing stays off
Webhooks__Enabled=false
OutboxProcessor__Enabled=false
NotificationFanoutProcessor__Enabled=false
Scheduler__Quartz__Enabled=false
EmailDispatchProcessor__Enabled=false
EmailDispatchRabbitMq__Enabled=false
MCP_ENABLED=false

# Base Application URLs
PUBLIC_BASE_URL=https://events.example.org

# Authentication (Local Identity; the standalone default)
# See: ../security-and-identity/authentication.md
AUTHENTICATION_PROVIDER=local
AUTHORIZATION_PROVIDER=local
AUTHENTICATION_LOCAL_JWT_KEY=replace-with-output-from-openssl-rand-base64-64
IDENTITY_DATABASE_TOPOLOGY=colocated

# Operator Legal Identity (Optional first-run setup pre-seed; can also be configured in /setup)
# See: ../configuration-and-operations/environment-variables.md#9-operator-legal-identity-production-gate
INSTANCE__OPERATORIDENTITY__OPERATORID=01912a7e-1234-7000-8000-000000000001
INSTANCE__OPERATORIDENTITY__PUBLICNAME=Community Events Foundation
INSTANCE__OPERATORIDENTITY__LEGALNAME=Community Events Foundation Non-Profit
INSTANCE__OPERATORIDENTITY__ISOFFICIALINSTANCE=false
INSTANCE__OPERATORIDENTITY__OFFICIALORIGIN=https://events.example.org
INSTANCE__OPERATORIDENTITY__OPERATORKINDCODE=unincorporated_association
INSTANCE__OPERATORIDENTITY__JURISDICTIONCOUNTRYCODE=US
INSTANCE__OPERATORIDENTITY__PUBLICCONTACTEMAIL=contact@example.org
INSTANCE__OPERATORIDENTITY__WEBSITEURL=https://example.org
INSTANCE__OPERATORIDENTITY__LEGALNOTICEURL=https://example.org/legal
INSTANCE__OPERATORIDENTITY__TERMSURL=https://example.org/terms
INSTANCE__OPERATORIDENTITY__PRIVACYURL=https://example.org/privacy
```

#### Bounded SQLite processing profile

Keep the optional-processing settings above for this Standalone recipe. Local webhooks are otherwise enabled by default, and their current SQLite readiness queries can fail with an unsupported timestamp comparison. The event-directory query correction does not repair webhook readiness. Disabling SMTP alone does not avoid that separate failure.

This bounded profile is verified for directory and administrator HTTP access, email health, and persisted credential, bootstrap and key continuity. It does not run webhook delivery, queued outbox/email/notification processing or Quartz jobs. Scheduled maintenance and cleanup therefore do not run, and workflows that need asynchronous completion must not be treated as operational under these settings. Read-time privacy expiry is not a substitute for physical cleanup.

Before relying on those features, verify their database/worker configuration separately and deliberately enable the required processors. Enabling persisted email delivery does not start processors disabled here. Do not hide the webhook failure by changing readiness results or disabling database/security checks.

Operator legal identity can either be pre-seeded through the environment variables above or configured interactively during first-run onboarding at `/setup`. The operator kind must match your actual legal status; `community` is not an accepted code. The HTTPS `OFFICIALORIGIN` is required even for an unofficial instance. For localhost evaluation, use your intended operator HTTPS origin for that identity field and `http://localhost:8080` for `PUBLIC_BASE_URL`. Generate the signing key with `openssl rand -base64 64`, store it only in the selected secret authority, and retain it across recreation. Do not add SMTP credentials to make setup pass. With Infisical, select and configure that authority explicitly; it does not fall back to environment secrets.

### Step 3: Run the Container

```bash
docker run -d \
  --name islamu-event-standalone \
  --restart unless-stopped \
  --env-file .env \
  --mount source=event_standalone_data,target=/app/data \
  -p 127.0.0.1:8080:8080 \
  ghcr.io/islamu-ngo/event-standalone:latest
```

*(Alternatively, build from source: `docker build -t islamu/event-standalone -f src/Event.Standalone/Dockerfile .`)*

### Multi-Platform & Host OS Compatibility

ISLAMU Event standalone images are published as **multi-architecture OCI manifest lists (Image Indexes)**. You pull a single image tag and your Docker daemon automatically selects and runs the matching native binary slice:

```bash
docker pull ghcr.io/islamu-ngo/event-standalone:latest
```

| Host Environment                               | Supported Architectures                      | Execution Model & Operational Notes                                                                                                                                                                                                                                                                                                     |
| ---------------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Linux Servers & VPS**                        | `linux/amd64`, `linux/arm64`                 | **Native execution.** Supports standard x86\_64 Intel/AMD servers and 64-bit ARM cloud instances (e.g., AWS Graviton, Hetzner CAX series, Ampere Altra, Raspberry Pi 4/5) with zero CPU emulation overhead.                                                                                                                             |
| **macOS (Apple Silicon & Intel)**              | `linux/arm64` (M1–M4), `linux/amd64` (Intel) | **Native container performance.** Runs inside the standard macOS Linux virtualization layer (Docker Desktop, OrbStack, Colima). Apple Silicon Macs automatically pull and execute `linux/arm64` directly on the M-series CPU without Rosetta 2 translation penalties.                                                                   |
| **Windows (WSL2 / Docker Desktop)**            | `linux/amd64`                                | **Native Linux VM execution.** Fully supported via Docker Desktop with the WSL2 backend or a Hyper-V Linux VM. Docker automatically pulls the `linux/amd64` variant and runs it within the lightweight Linux utility VM.                                                                                                                |
| **Windows Server (Native Windows Containers)** | *Not Supported*                              | The container packaging uses Linux base images (`mcr.microsoft.com/dotnet/runtime-deps:10.0-noble-chiseled-extra`). Native Windows container mode (`Windows Server Core` / `NanoServer`) is not used. For native Windows Server environments without Linux containers, host the application directly as a system service or behind IIS. |

> \[!TIP] **Single Tag Convenience:** You do not need to manage or specify architecture tags (such as `:latest-arm64` or `:latest-amd64`). The container registry resolves the target CPU transparently on `docker pull` or `docker run`.

***

## 3. Container Startup & File Layout

When the container launches:

1. It applies migrations and seeding for the primary SQLite database (`/app/data/islamu_event.db`).
2. It initializes the separate [GDPR Privacy-Erasure authority store](/islamu-event/documentation/readme/security-and-identity/privacy-erasure.md) (`/app/data/privacy_erasure_authority.db`).
3. It persists Data Protection keys in the primary SQLite database, not a separate key directory. With the configuration above, uploaded media lives at `/app/data/storage`.
4. It starts the internal Kestrel web server and binds port `8080`.

Verify container startup logs:

```bash
docker logs -f islamu-event-standalone
```

Check readiness at `http://localhost:8080/health` and liveness at `/alive`. Disabled email can report Healthy. Enabled but unconfigured email, or a reported SMTP connection failure, reports Degraded without making otherwise healthy core readiness return HTTP 503. Required database, security and authority failures still block startup or report Unhealthy; investigate those rather than disabling their checks. A successful readiness response is not proof of email receipt. The expected healthy-core result assumes the bounded optional-processing configuration above, not the unchanged default Local-webhook configuration.

***

## 4. First-Run Setup Wizard

Once the container is healthy:

1. Retrieve the generated setup secret from the Docker host in a private terminal:

   ```bash
   umask 077
   docker cp islamu-event-standalone:/app/data/setup-secret ./setup-secret
   cat ./setup-secret
   ```

   If you supplied `SETUP_SECRET` explicitly, use that value instead; a generated file is not expected. Never paste it into logs or support tickets.
2. Navigate to `http://localhost:8080/setup` (or `https://events.example.org/setup` behind your reverse proxy).
3. Validate the secret, choose **Continue Local setup**, and complete the instance details and operator legal identity (which can be copied directly to the directory operator identity). Enter the initial administrator's username and temporary password; credential email is optional. This setup-authorized provisioning does not require an existing sign-in or public **Create an account** flow.
4. Sign in with that temporary credential and complete the required private password replacement, then sign in afresh. The temporary credential cannot establish an ordinary session. No verification message is needed for Local administrative handover.
5. After completion the setup flow is locked and the generated secret file is removed. Delete your host copy with `rm -f ./setup-secret`.

The profile's **Support email** is public site identity, not the account's credential email or an SMTP From address. Saving it does not configure a sender or enable delivery; use the separate SMTP administration when email is wanted.

Create subsequent Local accounts through `/settings/instance?section=local-accounts`, using current instance-administrator access. That screen collects email and profile details, but hands over the generated temporary password privately rather than mailing it. Creation and reset both require the recipient to replace the credential before normal sign-in. Tenant administration alone cannot provision these shared credentials. See the [Local accounts runbook](/islamu-event/documentation/readme/administration-and-branding/admin-guide.md#local-accounts) for one-time disclosure and lost-response recovery.

If you deliberately select Keycloak or AT Protocol, use that provider's sign-in and verification procedures. Turning off Event email does not turn off the provider's verification or account policy. For automated initial provisioning, see [configured administrator setup](/islamu-event/documentation/readme/self-hosting/docker-compose.md#option-b-headless-automated-onboarding).

### Optional Email

Leave email disabled for zero-email operation. To add it later, save non-secret SMTP settings in instance administration and explicitly enable delivery there; credentials belong in the selected secret authority. Existing persisted `email.delivery_enabled` remains authoritative across restarts: environment values, a setup manifest and starting a mail container are not bypasses. Disabling active delivery uses the guarded preview/confirmation workflow.

Mailpit is not included in this image. The optional Compose `mail` profile is private local capture for applications on its container network; its host SMTP port is deliberately not published. A separately launched Standalone container does not automatically share that network. Use the [SMTP and local capture guide](/islamu-event/documentation/readme/communications-and-notifications/email-smtp.md) only when you intentionally add email.

***

## 5. Reverse Proxy Configuration

In production, place the standalone container behind a TLS-terminating reverse proxy on port `8080`.

### Caddy Example

```caddy
events.example.org {
    reverse_proxy 127.0.0.1:8080
}
```

### Nginx Example

```nginx
server {
    listen 443 ssl http2;
    server_name events.example.org;

    ssl_certificate /etc/letsencrypt/live/events.example.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/events.example.org/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
```

***

## 6. Backup and Recovery

### Relocating uploads from an earlier default

Older Standalone images without an explicit root stored uploads in `storage-data/local` relative to the application's working directory, outside the default data volume. Changing the default does not move those files.

1. Before removing the old container, stop incoming writes and all workers that can change or delete storage. Record the old effective root and take a coordinated database, object and key-authority backup.
2. With the old container stopped but retained, copy its entire local-storage directory to `storage` inside the durable `/app/data` volume, or to your explicitly configured durable root. Preserve every relative path: stored object keys do not change. Use host-side/container-volume tools; the chiseled application image has no shell.
3. Compare the copied file counts, sizes and checksums. Resolve conflicting files rather than overwriting them blindly, and ensure the non-root container user can read and write the destination.
4. Start the replacement with the chosen root while normal traffic remains closed. Reconcile metadata and bytes in dry-run mode and verify representative authorized downloads before reopening writes or enabling cleanup.

Keep the source copy and backup until verification succeeds. If bytes are missing, stop and repair the copy or select the preserved durable root explicitly; do not delete database records to make reconciliation appear healthy. An existing explicit root remains in effect and needs no relocation solely because this default changed.

### Coordinated backup

Back up the primary database (including Local Identity and Data Protection keys), media and independent privacy-erasure authority consistently. Preserve the selected signing/secret authority separately. The shipped container is chiseled: do not assume it contains a shell or the `sqlite3` command.

For a simple stopped-writer capture into an access-restricted host directory:

```bash
umask 077
mkdir standalone-backup
docker stop islamu-event-standalone
docker cp islamu-event-standalone:/app/data/. ./standalone-backup/
# Restart only after the copy completes successfully.
docker start islamu-event-standalone
```

> \[!CAUTION] **Capture together does not mean roll back together.** Keep the newest verified privacy-erasure authority independently of any primary database rollback. Do not replace it with the older authority copy from a historical whole-volume backup; that can discard later erasures. Include required SQLite WAL companions when preserving files and follow the [Privacy Erasure](/islamu-event/documentation/readme/security-and-identity/privacy-erasure.md) replay gates before reopening traffic.

Store the capture encrypted outside the container host and rehearse recovery in isolation. A persistent volume is not a backup, and key persistence alone does not prove crash recovery, a consistent live snapshot or survival of every session.

{% hint style="info" %}
**Operating a community deployment?** ISLAMU Event is 100% free and open source. If this standalone deployment powers events for your organization, please consider [supporting our work via Stripe](https://donate.stripe.com/14A6oIesc0Oc2KYg35aR200) or [becoming an official sponsor](/islamu-event/documentation/readme/contributing/sponsorship.md).
{% endhint %}

***

## Related Guides & Next Steps

* [**First-Run Administration Guide**](/islamu-event/documentation/readme/administration-and-branding/admin-guide.md) — Complete the web onboarding wizard at `/setup`.
* [**Deployment Tiers & Sizing**](/islamu-event/documentation/readme/self-hosting/deployment-tiers.md) — Review capacity guidelines and hardware sizing.
* [**Docker Compose Runbook**](/islamu-event/documentation/readme/self-hosting/docker-compose.md) — Scale up to split PostgreSQL and Keycloak containers when ready.
* [**Troubleshooting & Operational Health**](/islamu-event/documentation/readme/configuration-and-operations/troubleshooting-and-health.md) — Fast solutions for setup secret retrieval, TLS issues, and container errors.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://islamu.gitbook.io/islamu-event/documentation/readme/self-hosting/docker-standalone.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
