No service was installed on the development laptop. contentd.service is a
deployment example for a dedicated host with systemd and an existing PostgreSQL
server. The HTTP listener defaults to loopback. Put a TLS-terminating reverse
proxy in front; restrict network access there. Authentication remains out of scope,
so do not mistake operational hardening for permission to expose writes publicly.
bin/build on the target architecture. Copy go-service/contentd
into a versioned directory under /opt/contentd/releases/. Keep the prior binary.contentd. Create a PostgreSQL database
and a separate migration-owner role. Back up existing data before schema changes.migrate command with the owner's DATABASE_URL.
It uses a transaction, advisory lock, migration version and SHA-256 checksum.
Repeating it is safe. It refuses existing untracked blog tables; this is not an
automatic live-Django cutover procedure. Serving never runs migrations.content_schema_migrations. It needs
no CREATE, DROP, DELETE, or superuser rights. Configure local peer authentication
or a protected password file. For a remote database use verified TLS./etc/contentd/environment, mode
0600, populated from environment.example. Point current to the new release.
Adjust hosts and paths deliberately. Start the service and check /readyz.The harness's assignment_harness_* databases and ownership marker are only for
tests. Do not use the harness to initialize, seed, or benchmark a real database.
The production migrator can initialize a normal database without that marker.
Set DATABASE_URL explicitly for both migration and serving on a production host;
when unset or empty, both commands use dbname=assignment_harness_native.
| Setting | Compatibility | Production, default |
|---|---|---|
| Legacy framework errors | Captured plaintext Python errors for observed cases | Generic JSON 500, no traceback |
| Required configuration | None; database defaults to assignment_harness_native | Exact ALLOWED_HOSTS; same database default |
| Schema check at startup | Existing compatible tables | Version 1 and matching checksum required |
| Health endpoints | Absent | GET /healthz and /readyz |
| Request body limit | Reference behavior | 1 MiB, 413 on overflow including chunked input |
| Request admission | Unbounded handler admission | 64 active application handlers, excess gets 503 |
| SQL connections | 8 by default | 8 by default, configurable 1–128 |
| HTTP deadlines | Reference comparison profile | Headers 5s, read/query context 15s, write 30s, idle 60s |
| Header size | Go default | 32 KiB nominal limit plus Go parser allowance |
| Logs | Startup/shutdown, optional SQL diagnostics | JSON request ID, method, status and elapsed time |
| Proxy headers | Unused | Untrusted and ignored; Host checked directly |
Both profiles preserve partial writes, draft visibility, the non-atomic counter, response shape and ordering rules. Production errors and operational limits are explicit differences, not a claim that the production profile passes unchanged debug-output golden files. Request deadlines can cancel database operations but do not undo already committed writes. The saved Python errors in compatibility mode are emulation assets, not genuine Go stack traces.
DB_MAX_CONNS and MAX_INFLIGHT have validated bounds. Invalid profiles, absent
required settings, unreachable databases and incompatible schema fail startup.
SQL_TRACE is rejected in production. Logs omit bodies, SQL bind values and query
strings. Incoming request IDs are replaced rather than trusted.
Liveness checks the HTTP process. Readiness pings PostgreSQL with a one-second deadline; it is not a continuous schema-drift check or dependency-wide health audit. Use an allowed Host for probes. The app stops accepting connections on SIGTERM and waits up to ten seconds for handlers before forcing closure. systemd permits fifteen seconds. Black-box tests hold a row lock, send a real request and SIGTERM, release the lock, then verify a successful response and zero-exit shutdown.
For this first schema version, rollback means stop, repoint current at the prior
compatible binary, start, and verify readiness. Future schema changes need explicit
backward-compatibility and restore procedures. No automatic destructive down
migration or database deletion is provided.
The unit limits privileges, writable paths, process count, descriptors and memory. Its 1 GiB cap is an example, not a measured production capacity recommendation. Unpaginated results can still consume substantial memory. Verify backup restoration, TLS/proxy settings, logging retention, capacity and monitoring on the actual host before exposing the application.