Deployment & Docker
Container status commands
# Is it actually running, or crash-looping/restarting?
sudo docker ps -a
# Full state detail: status, restart count, exit code, last error
sudo docker inspect <container> --format '{{.State.Status}} restarting={{.State.Restarting}} exitcode={{.State.ExitCode}} error={{.State.Error}}'
# Live resource usage - a dead/crash-looping container shows all-zeros
# here, not "idle" - don't mistake 0% CPU/0B memory for healthy
sudo docker stats --no-stream
# Docker's own healthcheck verdict - more reliable than "is the process
# running", catches app-level failures too
sudo docker inspect <container> --format '{{.State.Health.Status}}'Container endlessly restarting: architecture mismatch
Symptom: the container status shows endless restarts, docker stats
shows all-zeros, and logs show something like exec format error.
Cause: a CPU architecture mismatch between the host and the pulled image — e.g. an ARM host pulling an image that was only ever built for x86_64 (a common trap on ARM-based cloud free tiers). A binary built for one architecture cannot execute on a kernel of a different architecture.
Diagnose:
uname -m
sudo docker image inspect <image> --format '{{.Architecture}}'
# mismatch here = exec format error on every startFix: build and publish a genuinely multi-arch image (e.g. via Docker Buildx with QEMU for cross-compiling), so the same tag resolves to the correct platform variant automatically wherever it’s pulled. A JVM-based app makes this cheap — the compiled bytecode itself is architecture-independent, only the base runtime image needs to match each target platform, so a CI split of “compile once” → “package once per platform” avoids recompiling per architecture.
A true multi-arch manifest generally can’t be built once and exported to a portable artifact for a separate job to push later — the build tool usually needs to build and push together to assemble the manifest correctly. Check your specific tooling’s constraints before assuming a build/push split is possible for this step.
CI step fails with “Permission denied” on a wrapper script
Symptom: a CI step invoking ./mvnw (or any committed wrapper script)
fails immediately with Permission denied.
Cause: the file’s executable bit wasn’t preserved in the git object — common when the file is ever re-saved on a platform that doesn’t track Unix file permissions. Checkout actions generally preserve exactly whatever file mode is stored in git.
Fix: add an explicit chmod +x immediately before invoking the script
in the workflow step, rather than trying to fix the file mode in git
history — simpler, and doesn’t depend on how or where the file was
originally committed.
Docker “permission denied” on docker.sock
Symptom: running a docker command without sudo fails with a
permission error, even after adding your user to the docker group.
Cause: group membership changes only take effect in a new login session — an already-open SSH session doesn’t pick it up.
Fix: either prefix commands with sudo, or start a completely fresh
session:
exit
ssh <your-connection>
docker ps -a # should now work without sudo