Restart policy & container settings
Choose what Docker does when your app stops, override the start command, and set Linux capabilities, sysctls, extra groups, an init process, GPU access and zero-downtime deploys.
Every app and service on your server runs in a Docker container. Synq picks sensible defaults for it, and
most apps never need anything else. This page covers the knobs for the cases that do: an app that should
not come back after it exits, a program that needs an extra Linux permission, a kernel setting such as
net.core.somaxconn, a model server that needs the GPU, or an app that must never run twice at the same time.
Everything here applies the next time the container is created, so save and then redeploy.
What you need first
- A project that runs a container on one of your own servers. (A static site has no container, so it has none of these settings.)
- Permission to change the project's settings.
Where the settings are
| Project shape | Where |
|---|---|
| A single app | Project → Advanced → expand the Container card. It has Command, Cap Add, Cap Drop, Sysctls, Groups, Init, GPU, Zero downtime, Restart policy and, for On failure, Max retries. Press Save. |
| A service in a compose or monorepo project | Apps & Services → the service → Settings. The Runtime section has Command and Restart policy; the Container section has Cap Add, Cap Drop, Sysctls, Groups, Init and GPU. Press Save changes. |
Restart policy
The restart policy tells Docker what to do when the app's process exits or the server reboots.
| Option | What happens |
|---|---|
| Always | Restart whenever the process stops, and start again when Docker or the server restarts. The default for a single app. |
| Unless stopped | Like Always, but a container you stopped yourself stays stopped, even after a server reboot. The default for services. |
| On failure | Restart only after a crash (a non-zero exit code), up to Max retries times (1 to 100, default 5). A clean exit stays stopped. |
| No | Never restart. The container stays stopped until you start or redeploy it. |
Pick On failure or No for a one-shot task (a migration, an import script) that should run and then stop, instead of looping forever. Keep Always or Unless stopped for anything that serves traffic.
A crash loop is still a crash loop
A restart policy only restarts the container. If the app exits because of a bad variable or a missing database, it keeps failing. Open the project's Logs to see why, and fix the cause.
In a docker-compose.yml, the restart: key of a service sets the same thing (always, unless-stopped,
on-failure, no).
Command
Command replaces the start command Synq detected. It runs through sh -c, so you can chain steps:
npx prisma migrate deploy && node server.jsLeave it as detected to keep the default. For an image built by a builder such as Nixpacks, Railpack or Buildpacks, the image's own start command is used.
Linux capabilities (Cap Add, Cap Drop)
Docker starts a container with a reduced set of Linux capabilities. Some programs need one more, for example
a VPN needs NET_ADMIN, and hardening often means dropping everything the app doesn't use.
- Cap Add: extra capabilities, separated by commas or spaces, for example
NET_ADMIN, SYS_TIME. - Cap Drop: capabilities to remove from Docker's default set.
ALLdrops every one.
Write the names with or without the CAP_ prefix, in any case. Synq checks each one against the list of
capabilities the Linux kernel defines and refuses unknown names. Up to 64 in each field.
A common hardening pattern for a web app that listens on a high port:
| Field | Value |
|---|---|
| Cap Drop | ALL |
| Cap Add | NET_BIND_SERVICE (only if it must listen on a port below 1024) |
Sysctls
Kernel parameters for this container only, one key=value per line:
net.core.somaxconn=1024
net.ipv4.tcp_keepalive_time=600Only parameters the kernel keeps per container can be set this way: net.*, fs.mqueue.*,
kernel.shm*, kernel.msg* and kernel.sem. Anything else (for example vm.max_map_count, which
Elasticsearch asks for) applies to the whole server; Synq refuses it here, because Docker would fail to start
the container. Set those on the server itself, for example in /etc/sysctl.d/. Up to 64 entries.
Groups
Supplementary groups for the user the container runs as: group names or numeric GIDs, separated by commas or
spaces, for example 999 to let the app read a socket owned by that group. Up to 32.
Init
Turn on Init to run a small init process (tini) as PID 1. It forwards signals such as SIGTERM to your
app and cleans up zombie processes. Use it when your app doesn't handle signals itself (a shell script as
the entry point, for example) and takes the full stop timeout to shut down, or when it leaves defunct child
processes behind.
GPU
GPU passes NVIDIA GPUs into the container, the same as docker run --gpus:
| Option | What the container gets |
|---|---|
| Off | No GPU. The default. |
| All GPUs | Every GPU on the server (--gpus all). |
| Specific count | That many GPUs, 1 to 64 (--gpus 2). Docker picks which ones. |
The server needs an NVIDIA driver and the NVIDIA Container Toolkit. After installing the toolkit, register it with Docker and restart Docker:
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart dockerBefore each build, Synq asks the server's Docker whether it has the nvidia runtime. If it doesn't, the
deploy stops right away with a message that names the server, instead of failing when the container is
created. docker info on the server lists the runtimes Docker knows (look for nvidia under Runtimes).
GPU access needs a container on one of your own servers. A deploy that asks for a GPU on Synq Cloud, a Kubernetes cluster or the bare runtime is refused, because the GPU could not be passed through.
In a compose file, Synq reads the GPU reservation Docker documents:
services:
model:
image: vllm/vllm-openai
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all # or a number, such as 1
capabilities: [gpu]Picking GPUs by device_ids is not supported; use count.
Zero downtime (single apps)
On by default. A deploy starts the new container beside the old one and switches traffic once the new one is ready, then stops the old one.
- It needs the Container IP routing mode (Advanced → Routing). The default, Auto, uses a Loopback port; the new container can't bind the same port, so with it the old container always stops first, whatever this switch says.
- An app that publishes any host port (in its Ports editor) always stops the old container first, because a fixed port on the server can't be bound by two containers at once. The deploy log says so.
- Turn it off for apps that must never run twice at once: a bot that holds a single connection, a scheduler that would run jobs twice, an app that writes to a SQLite file.
What isn't supported
Synq reads cap_add, cap_drop, sysctls, group_add, init, GPU reservations (deploy.resources.reservations.devices),
restart, command and entrypoint from a compose file. The following Docker options are not available, and a compose file that uses them
shows a warning when it's scanned:
| Option | What happens instead |
|---|---|
ulimits | Docker's daemon defaults apply. |
privileged | The container runs unprivileged. |
devices | No host devices are passed through. For GPUs, use GPU above. |
security_opt | The default seccomp and AppArmor profiles apply. |
shm_size | /dev/shm stays at Docker's 64 MB default. |
user, working_dir | The image's own user and working directory are used. |
hostname | The host name is set to the service name. |
ipc, pids_limit, oom_score_adj, runtime | Docker's defaults apply. |
Need higher open-file limits?
Since per-container ulimits aren't available, raise the default for every container on the server in
/etc/docker/daemon.json, then restart Docker:
{
"default-ulimits": {
"nofile": { "Name": "nofile", "Soft": 65536, "Hard": 65536 }
}
}Restarting Docker restarts every container on that server, so do it in a quiet moment. Containers get the new limit when they are created, so redeploy the apps that need it.
From the API
A single app's container settings are part of the project update:
curl -X PATCH -H "Authorization: Bearer $SYNQ_TOKEN" -H "Content-Type: application/json" \
-d '{"runtimeOptions":{"restartPolicy":"on-failure","restartMaxRetries":3,"capDrop":["ALL"],"capAdd":["NET_BIND_SERVICE"],"sysctls":{"net.core.somaxconn":"1024"},"init":true}}' \
"$SYNQ_URL/api/projects/$PROJECT_ID"Keys are merged one by one; send null for a key to go back to its default. gpus takes "all" or a
number from 1 to 64, and null turns GPU access off. SYNQ_URL is your panel address
(for example https://app.example.com).
Related guides
Replicas and autoscaling
Run several copies of an app on one server with the edge balancing requests across them, and let Synq add or remove copies with the load.
Maintenance mode
Show visitors a "down for maintenance" page on every domain of a project, instantly and without a redeploy, while the app keeps running behind it.