- Documentation
- Deployments
- Container Runtime
Container Runtime
When Guara Cloud deploys your service, the container runs inside a secure, hardened environment. This page describes how the runtime works and what you need to know when writing your Dockerfile.
Non-root execution
All containers run as a non-root user with UID 1000 and GID 1000. This is enforced by the platform and cannot be changed.
This means:
- Your application process will always run as user
1000, regardless of anyUSERdirective in your Dockerfile. - Files inside your image must be readable by all users (permissions
644for files,755for directories) — this is the default for most Docker images. - The platform drops every Linux capability except
NET_BIND_SERVICE, so UID 1000 can bind a configured low port. Port8080remains the recommended default for image portability.
Dockerfile best practices
When writing a Dockerfile for Guara Cloud, keep these guidelines in mind:
# Use a non-root user in your image for consistency
FROM node:20-alpine
# Create a non-root user (optional but recommended)
RUN addgroup -g 1000 appgroup && adduser -u 1000 -G appgroup -D appuser
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
# Ensure files are world-readable (default for COPY)
# Avoid restrictive permissions like chmod 700
# Use an unprivileged port
EXPOSE 8080
# Set the user (Guara Cloud will override to UID 1000 regardless)
USER appuser
CMD ["node", "server.js"]
Filesystem
For your applications, the container root filesystem is writable — but every write happens as the non-root user (UID 1000). You can only write where UID 1000 has permission, so paths owned by root (such as the directories the stock nginx image ships) are off-limits even though the filesystem is not globally read-only.
Platform-managed catalog services (databases, caches, brokers) run with a stricter, fully read-only root filesystem. That hardening does not apply to your own app containers.
Writable paths
To make common workloads (nginx caches, PID files, logs, temp data) work out of the box, Guara Cloud mounts the following group-writable ephemeral volumes — writable by UID 1000 regardless of the base image’s ownership:
| Path | Purpose | Size limit (all profiles) |
|---|---|---|
/tmp | Temporary files, caches, session data | 256 MiB |
/var/run | PID files, Unix sockets | 64 MiB |
/var/cache | Application caches (nginx, package, etc.) | 256 MiB |
/var/log | Application log files | 128 MiB |
Common scenarios
If your application tries to write to a root-owned path outside these mounts, you will see a Permission denied error. Here are common solutions:
| Framework/Tool | Default write path | Solution |
|---|---|---|
| Next.js | .next/cache | Set NEXT_CACHE_DIR=/tmp/next-cache environment variable |
| nginx | /var/cache/nginx | Already writable via /var/cache |
| PHP | /tmp for sessions | Already writable |
| Python/pip | ~/.cache/pip | Set PIP_CACHE_DIR=/tmp/pip-cache |
| Node.js/npm | ~/.npm | Set npm_config_cache=/tmp/npm-cache |
| General | Application-specific | Redirect writes to /tmp via environment variables or flags |
Security restrictions
Guara Cloud enforces a strict security policy on all containers:
| Restriction | Description |
|---|---|
| No privilege escalation | Containers cannot gain additional privileges at runtime |
| Capabilities minimized | All capabilities are dropped except NET_BIND_SERVICE; no mount or network-admin access |
| Seccomp profile enforced | Only safe system calls are allowed (RuntimeDefault profile) |
| No root execution | The container process always runs as UID 1000 |
| Non-root writes only | Root FS is writable, but only as UID 1000 — root-owned paths are read-only |
| No service account token | Containers have no access to Kubernetes API credentials |
These restrictions follow the Kubernetes Pod Security Standards (PSS) restricted profile, the highest level of container security available.
Port configuration
Your application must listen on the port configured in the service settings (default: 8080). Guara Cloud injects the PORT environment variable with the configured value.
// Example: read the PORT environment variable
const port = process.env.PORT || 8080;
app.listen(port);
Summary
| Property | Value |
|---|---|
| Runtime user | UID 1000 / GID 1000 |
| Root filesystem | Writable, but only as UID 1000 (read-only for catalog services) |
| Writable paths | /tmp, /var/run, /var/cache, /var/log (group-writable mounts) |
| Privilege escalation | Disabled |
| Linux capabilities | All dropped except NET_BIND_SERVICE |
| Seccomp profile | RuntimeDefault |
| Default port | 8080 (configurable) |