Set up your repository
Make an application portable to Autodisc with explicit runtime, network, health, database, and migration contracts.
Autodisc can detect common runtimes, but a production repository should make its deployment contract explicit. The important work is not “using the AI agent.” It is making the repository portable: one build, one start command, declared ports, environment-based dependencies, and a health signal.
This guide shows the same changes a developer can make by hand, in a pull request, without managed AI.
The repository contract
An Autodisc-ready service must:
- build without interactive input;
- start with one deterministic command;
- listen on
0.0.0.0and the configured port; - read credentials and environment-specific URLs from environment variables;
- expose a lightweight health endpoint when it serves HTTP;
- write logs to standard output and standard error;
- stop cleanly on
SIGTERM; and - keep database migrations separate from every replicated app process.
The same contract works locally, in CI, and on another container platform.
1. Make the server portable
Do not bind only to localhost, and do not hardcode a production port.
const port = Number(process.env.PORT ?? 3000);
const server = app.listen(port, "0.0.0.0", () => {
console.log(`api listening on ${port}`);
});
process.on("SIGTERM", () => {
server.close((error) => {
process.exit(error ? 1 : 0);
});
});Add a health endpoint that proves the process can serve requests. Keep it fast and avoid mutating data.
app.get("/health", (_request, response) => {
response.status(200).json({ status: "ok" });
});A liveness endpoint should normally report process health. If you also need to check a database or queue, expose a separate readiness endpoint so a temporary dependency failure does not create a restart loop.
2. Move environment-specific values out of source
Commit variable names and safe local defaults. Do not commit credentials.
# .env.example
PORT=3000
DATABASE_URL=postgresql://user:password@localhost:5432/app
REDIS_URL=redis://localhost:6379/0
PUBLIC_APP_URL=http://localhost:3000Application code should fail clearly when a required value is absent:
function required(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`${name} is required`);
return value;
}
const databaseUrl = required("DATABASE_URL");In Autodisc, enter secret values in the selected environment's Variables settings. A staging value does not automatically become a production value.
3. Containerize deliberately
An explicit Dockerfile gives the repository one repeatable build definition. This Node example installs from the lockfile, creates a production build, and runs as a non-root user:
FROM node:24-bookworm-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:24-bookworm-slim AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=build /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]Also add a .dockerignore:
.git
.env
.env.*
!.env.example
node_modules
dist
coverageTest the same artifact locally before connecting the repository:
docker build -t my-api:local .
docker run --rm -p 3000:3000 \
-e PORT=3000 \
-e DATABASE_URL=postgresql://user:[email protected]:5432/app \
my-api:local
curl --fail http://localhost:3000/health4. Add autodisc.yml
The current CLI uses the version 1 single-service manifest:
version: "1"
name: my-api
source:
type: repo
repo_full_name: acme/my-api
repo_branch: main
runtime:
stack: dockerfile
dockerfile: Dockerfile
start_command: node dist/server.js
port: 3000
deployment:
plan_type: starter
public: true
auto_restart: true
environment:
NODE_ENV: productionUse environment only for non-secret defaults. Add secret values in the
dashboard. The CLI validates version, name, source.type,
deployment.plan_type, runtime.start_command, and the port range before
uploading or deploying.
Run:
autodisc login
autodisc deploy --project PROJECT_IDThe current CLI starts the deployment and reports its initial state. It does not yet follow the canonical rollout to a health-gated terminal state. Confirm the result in the project workspace before treating the release as successful.
Multi-service repositories
Keep each independently scalable process separate:
.
├── apps/
│ ├── api/
│ │ └── Dockerfile
│ └── web/
│ └── Dockerfile
├── workers/
│ └── email/
│ └── Dockerfile
└── autodisc.ymlThe source-connected preparation path accepts a service map:
services:
api:
dockerfile: apps/api/Dockerfile
workdir: apps/api
run: node dist/server.js
port: 3000
web:
dockerfile: apps/web/Dockerfile
workdir: apps/web
run: node server.js
port: 3001
email-worker:
dockerfile: workers/email/Dockerfile
workdir: workers/email
run: node dist/worker.js
internal: trueinternal: true describes a process with no public route. depends_on
describes startup ordering; it is not a substitute for connection retries.
The current autodisc deploy CLI schema is single-service. Do not assume it
will apply a multi-service manifest atomically. Connect the repository through
the project workspace and review the detected Application Plan until CLI
multi-service parity is released.
Managed databases
Source-connected projects can declare managed resources and bindings in the repository:
services:
api:
dockerfile: apps/api/Dockerfile
workdir: apps/api
run: node dist/server.js
port: 3000
resources:
customer-data:
type: postgresql
class: shared-small
bindings:
- source: resource.customer-data.connection_url
target: service.api.environment.DATABASE_URLResource keys and service keys are chosen by the repository. Autodisc maps the
stable resource type to the selected provider only at apply time. Supported
managed types are postgresql, mysql, mariadb, mongo, redis, and
libsql.
Resource definitions cannot contain passwords, connection URLs, or arbitrary provider fields. Those values are created by the platform and cross the boundary only through a named output binding.
To use PostgreSQL:
- change the application to read
DATABASE_URL; - commit migrations with the repository;
- declare or approve PostgreSQL in the intended Autodisc environment;
- bind its
connection_urloutput to the service'sDATABASE_URL; - run migrations once for the release; and
- deploy and verify real read/write behavior.
For Redis, use the same pattern with REDIS_URL. Never hardcode an internal
resource hostname, provider ID, username, or password in application source.
Release migrations
Do not run destructive migrations in every application replica's startup command. Prefer a release job or an explicitly invoked migration step:
{
"scripts": {
"migrate": "prisma migrate deploy",
"start": "node dist/server.js"
}
}Validate migrations against an isolated non-production database first. Back up production before irreversible changes and keep a forward-fix or rollback procedure with the release.
Repository readiness checklist
- The container builds from a clean checkout.
- The service listens on
0.0.0.0:$PORT. /healthreturns success from inside the container.- Required dependencies come from named environment variables.
.env.examplecontains names, not real credentials.- Logs go to standard output and do not contain secret values.
- The process exits cleanly after
SIGTERM. - Database migrations are deterministic and run once per release.
- Every public service has an intentional port.
- Workers and schedulers are internal.
- A non-production deploy has passed build, rollout, health, and functional verification.
What the agent adds
Managed AI can propose the repository changes above and return them as a Change Request. It does not define a different deployment system. Whether the code was written by a person or an agent, Autodisc applies the same repository, artifact, resource, approval, and deployment contracts.