Deploy and operate
Build one executable, configure it with environment variables, run migrations as a separate step and start the server. This page covers settings, migration safety and the security work your application owns.
Good to know
Released APIs follow the stability guarantee. Security behavior is covered by automated tests; your application still owns the deployment checks on this page.
#Ask your agent
Read https://cicadel.fauzaanu.com/en/0.2.7/howto/operations/index.md.
Prepare this application for [deployment target]. Build its executable and
embedded assets, validate production configuration, and plan migrations and backups.
Keep secrets out of source. Run checks and verify readiness before reporting success.#Build and run
From your application directory:
export CICADEL_DEBUG=false
export CICADEL_SECURE_COOKIES=true
export CICADEL_ALLOWED_HOSTS=app.example.com
go tool cicadel ui build
go build -trimpath -o bin/server .
bin/server check --deploy --json
# When this application has database migrations:
bin/server migrate
bin/server runserver- Templates, migrations and static files are embedded in the executable.
- Run migrations as a separate deployment step.
check --deploy --jsonprints structured deployment diagnostics, like Django’scheck --deploy.- The example Dockerfile builds a non-root executable image.
See the foundation guide for custom checks, command flags and statuses, and the development rebuild/restart workflow.
#Settings
Settings apply in this order, later ones winning:
- Typed defaults.
- Project assignments.
- Environment overrides.
Production requires debug off, allowed hosts and secure cookies. Debug mode does not show exception details to clients.
Invalid values fail validation without printing their contents.
settings.LoadWith supports isolated test overrides and copies the allowed-host slice.
#Environment variables
| Variable | Value |
|---|---|
DATABASE_URL | PostgreSQL connection URL. Supply it as a secret. |
CICADEL_SECRET_KEY | Signing key. Generate it with cicadel secretkey and supply it as a secret. |
CICADEL_SECRET_KEY_FALLBACKS | Old keys during rotation. See signed tokens. |
CICADEL_ADDRESS | Listen address. |
CICADEL_DEBUG | Debug mode. |
CICADEL_ALLOWED_HOSTS | Comma-separated exact hosts, without ports. |
CICADEL_SECURE_COOKIES | Secure cookie flag. |
CICADEL_STATIC_URL | A local path such as /static/ or an https CDN prefix. See static files. |
CICADEL_TRUSTED_PROXIES | Addresses of your reverse proxy. See behind a proxy. |
CICADEL_HSTS_SECONDS, CICADEL_SECURE_REDIRECT, CICADEL_CONTENT_SECURITY_POLICY | HTTPS policy. See security headers. |
CICADEL_MAX_BODY_BYTES | Request body limit, as a decimal integer. |
CICADEL_MAX_OPEN_CONNS, CICADEL_MAX_IDLE_CONNS | Database pool sizes, as decimal integers. |
CICADEL_READ_TIMEOUT, CICADEL_WRITE_TIMEOUT, CICADEL_IDLE_TIMEOUT, CICADEL_SHUTDOWN_TIMEOUT, CICADEL_CONN_MAX_LIFETIME | Go durations such as 15s or 30m. |
#Default limits
| Limit | Default |
|---|---|
| Request body | 1 MiB |
| Header/read timeout | 15 seconds |
| Write timeout | 30 seconds |
| Idle timeout | 60 seconds |
| Shutdown deadline | 10 seconds |
| Open database connections | 10 |
| Idle database connections | 5 |
| Connection lifetime | 30 minutes |
Tune these for your workload and database capacity.
Handlers must honor request cancellation. The shutdown deadline closes any remaining HTTP connections. The application owner closes its database after serving stops.
#Static files
The executable serves static files at content-hashed /static/<app>/ URLs.
To use a CDN instead:
- Run
go run manage.go collectstatic DIR. - Upload the output.
- Set
CICADEL_STATIC_URL=https://…/and switch traffic.
See static files.
#Behind a proxy
Terminate TLS at a reverse proxy. Set CICADEL_TRUSTED_PROXIES to the proxy’s
addresses.
- Cicadel then honors
X-Forwarded-ForandX-Forwarded-Protofrom those peers. - It ignores them from any other peer.
- It never trusts
ForwardedorX-Forwarded-Host.
Keep the original allowed Host. HSTS, the HTTPS redirect and the content security
policy are covered in security headers.
#Sessions
| Starter | Session table |
|---|---|
| Articles example | sessions |
| Accounts starter | accounts_browser_sessions, owned by the package, from migration 002 |
Sessions:
- are shared across instances;
- rotate at login;
- expire after 12 hours, or after 30 minutes idle;
- are invalidated at logout.
The accounts browser bridge checks revocation and account availability on every request. Custom user stores must implement their own revocation policy. A session ID alone is not an object permission policy.
#Migration safety
Each app keeps its version history table. Checksums live in the shared migration ledger. Recorded version dependencies decide the order. The canonical runner takes a shared PostgreSQL advisory lock.
Warning
Run migration commands over a direct or session-pooled database connection. Do not use transaction-mode PgBouncer.
What happens on failure:
- A failed transactional migration rolls back its schema and data changes. It is not marked as applied.
- A nontransactional migration can leave partial changes. Inspect them and write an explicit repair. Never blindly rerun destructive statements.
#Before destructive changes
- Take a database backup and test it.
- Prefer expand, backfill, contract changes that work with both the old and new app versions.
- Remember that Goose down migrations may be destructive.
Management commands use the checksum-aware runner. They roll back to an explicit target only with reviewed history and a data-loss acknowledgement. When needed, restore a tested backup or apply a reviewed forward repair.
Migration history is not a backup. The shared lock serializes migrations, but separate migrations do not make a whole deployment atomic.
#Your security responsibilities
#Queries and responses
- Pass user values as GORM parameters. SQL fragments, ordering, table names and raw queries are not safe when you pass arbitrary user strings into them.
- Select columns explicitly for JSON responses. Never serialize password hashes or whole user models.
#Sessions and CSRF
Apply middleware in this order:
- Session
LoadAndSave. - CSRF.
- Application authorization.
- Unsafe session-based requests need a
csrf_tokenform field or anX-CSRF-Tokenheader. This includes JSON writes. - Session pages are sent with
Cache-Control: no-store. - Authentication rotates the session and the CSRF token.
- Secure cookies require HTTPS, except in explicit local development.
- Add gateway or application rate limits to login and other expensive endpoints before you expose them publicly.
#Permissions
Every object change needs a current permission check. The articles example
restricts changes by author ID, both before rendering and in the SQL UPDATE
condition.
security.RequireLoginis low level. It only checks that a session exists.- Accounts-backed routes use
browser.RequireLoginwithbrowser.Middleware. It resolves current account availability and revocation. - The accounts package supplies password reset, password change and revocation. Verified recovery destinations, invitation policy and MFA integration stay application choices.
#Input and output
- The body limit also applies to forms.
- Multipart handlers must parse bounded input explicitly, remove temporary files, validate content and use the storage API instead of user-supplied paths.
- HTML templates escape ordinary strings. Trusted template types skip escaping. Use them only around developer-controlled content.
- Redirect helpers accept local absolute paths only.
#Check before deploying
go tool cicadel ui build
go tool cicadel ui check --json
go test -race ./...
go run manage.go check --deploy --json
go build -trimpath -o bin/server .Run database integration tests against a disposable PostgreSQL database. Keep secrets in your deployment configuration.
#Logging
observability.NewLogger(observability.Logging{Output: os.Stderr, Format: "json"})
builds a standard slog.Logger.
- Choose
textfor local development. - Pass a
slog.LevelVarto change the level at runtime. - Set
AddSourcefor caller locations. - Use
observability.Logger(ctx, logger)for request correlation. Pass the same logger to the request middleware.
Credential attributes are redacted by key before the optional ReplaceAttr hook
runs. Key redaction cannot clean free-form text, so never put credentials in
messages or errors. See logging.
#SMTP delivery
mail.Sender{Transport: mail.SMTP{Address: "smtp.example.com:587"}}This sends email over SMTP with:
- mandatory STARTTLS and certificate verification;
- context cancellation and a default 30-second deadline;
- plain text and HTML bodies encoded as MIME alternatives when both are present.
Supply credentials through secret configuration. Building a sender does not contact the SMTP server.
Delivery errors go back to the caller. Retries must allow for uncertain delivery after an SMTP connection failure. The application owns verified recovery destinations.
#Related guides
- Logging: named handlers, error alerts and generated-starter configuration.
- Email: attachments, envelopes and batch counts.
- Authentication: accounts, sessions and recovery.