Guides

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 shapeWhere
A single appProject → 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 projectApps & 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.

OptionWhat happens
AlwaysRestart whenever the process stops, and start again when Docker or the server restarts. The default for a single app.
Unless stoppedLike Always, but a container you stopped yourself stays stopped, even after a server reboot. The default for services.
On failureRestart only after a crash (a non-zero exit code), up to Max retries times (1 to 100, default 5). A clean exit stays stopped.
NoNever 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.js

Leave 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. ALL drops 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:

FieldValue
Cap DropALL
Cap AddNET_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=600

Only 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:

OptionWhat the container gets
OffNo GPU. The default.
All GPUsEvery GPU on the server (--gpus all).
Specific countThat 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 docker

Before 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:

OptionWhat happens instead
ulimitsDocker's daemon defaults apply.
privilegedThe container runs unprivileged.
devicesNo host devices are passed through. For GPUs, use GPU above.
security_optThe default seccomp and AppArmor profiles apply.
shm_size/dev/shm stays at Docker's 64 MB default.
user, working_dirThe image's own user and working directory are used.
hostnameThe host name is set to the service name.
ipc, pids_limit, oom_score_adj, runtimeDocker'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).

On this page