Crosstalk-Solutions/project-nomad · critical · Error
recreated container
Error message
recreated container ${readiness.reason} What it means
Thrown by DockerService after recreating a container when the new container fails a post-start readiness/health gate. The service starts a replacement container, waits for it to become healthy via _awaitContainerReady, and if readiness.ready is false it aborts the swap and throws, leaving the old container in place (the removal of the old container only happens after the gate passes).
Solutions
- Inspect readiness.reason in the thrown message to identify whether it's a timeout, unhealthy status, or connection refusal, then check `docker logs <new-container>` for the startup failure
- Increase the readiness/healthcheck timeout or fix the app's healthcheck definition if the service is just slow to start
- Verify the new image version is compatible with existing config/volumes (required env vars, DB migrations)
- Check for port conflicts or leftover containers binding the same port
- If the old container still runs, the system is safe; fix the root cause and re-trigger the recreate
Example fix
// before
await dockerService.recreateContainer('tipi') // throws: recreated container unhealthy
// after
try {
await dockerService.recreateContainer('tipi')
} catch (e) {
// old container is still running; diagnose the new one
console.error((e as Error).message) // 'recreated container <reason>'
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check image healthcheck definition before recreate
const info = await docker.getContainer(name).inspect()
if (!info.Config.Healthcheck) console.warn('No healthcheck defined; readiness gate may rely on port probe') Type guard
const isReadinessError = (e: unknown) =>
(e as Error).message.startsWith('recreated container') Try / catch
try {
await dockerService.recreateContainer(serviceId)
} catch (e) {
if ((e as Error).message.startsWith('recreated container')) {
// old container still serves traffic — log reason, alert, retry later
logger.warn('Recreate failed, old container retained:', (e as Error).message)
return
}
throw e
} Prevention
- Define a fast, accurate healthcheck in the service config so the readiness gate measures real readiness
- Keep startup time under the readiness timeout (pre-warm caches in build, not runtime)
- Test recreates on a staging host before production updates
When it happens
Trigger: Calling the container recreate/update flow (e.g. app update or reinstall) where the new container starts but its health check fails, times out, or the app inside never becomes reachable; readiness.reason carries the underlying cause (health status, timeout, port not listening).
Common situations: Updated image that crashes on boot, missing env vars/migrations for the new version, healthcheck misconfigured or too short a timeout, port conflicts preventing the new container from binding, slow image startup exceeding the readiness wait window.
Related errors
- Failed to fetch available versions for this service.
- Failed to get auth token from
- guard.blocked.join(' ')
- guard.warnings.join(' ')
- No token returned from
AI-assisted analysis of Crosstalk-Solutions/project-nomad@0bd1c6f4f9 (2026-08-27).
Data as JSON: /api/errors/36c1ff84ab446457.
Report an issue: GitHub.
Appendix: source
Thrown at admin/app/services/docker_service.ts:2231
Labels: {
...(containerConfig?.Labels ?? {}),
'com.docker.compose.project': 'project-nomad-managed',
'io.project-nomad.managed': 'true',
},
...(containerConfig?.User && { User: containerConfig.User }),
HostConfig: containerConfig?.HostConfig ?? {},
...(containerConfig?.ExposedPorts && { ExposedPorts: containerConfig.ExposedPorts }),
...(recreateEnv.length ? { Env: recreateEnv } : {}),
...(service.container_command ? { Cmd: service.container_command.split(' ') } : {}),
...(process.env.NODE_ENV === 'production' && {
NetworkingConfig: { EndpointsConfig: { [DockerService.NOMAD_NETWORK]: {} } },
}),
})
await newContainer.start()
// Health gate before discarding the old container.
const readiness = await this._awaitContainerReady(newContainer)
if (!readiness.ready) throw new Error(`recreated container ${readiness.reason}`)
if (oldInfo) {
const oldRef = await this._findContainerByName(oldName)
if (oldRef) await this.docker.getContainer(oldRef.Id).remove({ force: true })
}
service.installed = true
service.installation_status = 'idle'
await service.save()
this.invalidateServicesStatusCache()
return { success: true, message: `Service ${serviceName} reconfigured successfully` }
} catch (error: any) {
logger.error({ err: error }, `[DockerService] recreateCustomAppContainer failed for ${serviceName}`)
// Roll back: discard the failed new container and restore the renamed original.
try {
const failedNew = await this._findContainerByName(serviceName)
if (failedNew) {
const c = this.docker.getContainer(failedNew.Id)
await c.stop({ t: 5 }).catch(() => {})View on GitHub (pinned to 0bd1c6f4f9)