Eazler AI Lab AI Engineering Journal
Skip to article
Guides◷ 15 min readUpdated:

Ollama, Qdrant and Redis in Docker have no authentication by default

Bind an AI dev environment's ports to 127.0.0.1, and check your coding agent's sandbox and MCP approval defaults

On this page

A local AI development stack often combines Ollama for local models, Qdrant and Redis for retrieval-augmented generation (RAG), and a coding agent such as Claude Code or Codex CLI that edits files and runs commands, all on the same machine. According to the official docs, when Ollama, Qdrant and Redis run in Docker, their default configurations do not require authentication, and when Docker Compose publishes a port without a host IP, that port is open on every network interface. This article rewrites the “practical AI development environment setup guide” note first published in Korean on 2026-06-16, whose Compose example was exactly this setup. Instead of ranking tools, this version keeps only defaults that can be confirmed in today's official docs.

Korean original: 한국어 원문 (2026-06-16, rewritten 2026-10-06)Scope: Docker and Compose, Ollama, Qdrant, the official Redis and PostgreSQL images, Claude Code, Codex CLI, the MCP specificationSources checked: 2026-10-06

Summary

0.0.0.0
Where Compose binds a published port when no host_ip is set
every network interface · Compose file reference
3 of 4
Services in the June note's Compose example with no authentication by default
Ollama, Qdrant, Redis · each project's official docs
Off
Default for Claude Code's Bash sandbox
Codex CLI's sandbox is on by default with network off · checked 2026-10-06
Bottom line: In a Compose file for local AI development, bind ports to the loopback address only, as in "127.0.0.1:6333:6333". Docker's docs call port publishing “insecure by default,” and the official Ollama, Qdrant and Redis images accept requests without authentication. Binding to 127.0.0.1 does not stop a coding agent on the same machine from reaching these services, though. So set a Qdrant API key and a Redis password, and check what you are allowing when you open localhost access in an agent sandbox or approve a repository's .mcp.json.

Terms used here

TermMeaning
Port publishingMapping a port inside a container to a port on the host machine, set with docker run -p or Compose's ports:
Bind addressThe network address a server accepts connections on. 0.0.0.0 means every network interface on the machine; 127.0.0.1 (loopback) means the machine itself only
Protected modeA Redis feature that makes a Redis instance without a password refuse connections from outside loopback. The official Redis Docker image turns it off
MCP (Model Context Protocol)An open specification for how AI agents reach external tools and data. The side that provides tools is the MCP server; the agent side is the client. This article follows the 2026-07-28 revision of the specification
Project-scoped MCP serverIn Claude Code, an MCP server configuration written to .mcp.json at the repository root and shared with the team. Personal configurations are stored in ~/.claude.json
SandboxAn operating-system-level limit on which files and network destinations the shell commands run by a coding agent can reach

Ports exposed by the June note's Compose example

The June note included an example that started Ollama, Qdrant, PostgreSQL and Redis from one Compose file. All five ports were published without a host IP, as in "11434:11434". The Compose file reference says that if no host IP is set, the port “binds to all network interfaces (0.0.0.0).” The default for each service is below.

Service (port)Default authenticationOfficial documentation
Ollama (11434)NoneAuthentication docs: “The local API at http://localhost:11434 does not require authentication.” The desktop install binds to 127.0.0.1 only, but the official image's Dockerfile sets OLLAMA_HOST=0.0.0.0:11434, and Ollama's own Docker docs use -p 11434:11434
Qdrant (6333 REST, 6334 gRPC)NoneSecurity docs: “By default, an open source Qdrant deployment accepts requests from anyone who can reach it.” A self-hosted Qdrant binds to all interfaces, and the docs recommend binding to 127.0.0.1 for local development
Redis (6379)NoneOfficial image docs: protected mode is off so other containers can reach Redis easily, so publishing the port outside the host with -p leaves it “open without a password to anyone”
PostgreSQL (5432)Password requiredThe official image requires POSTGRES_PASSWORD and asks for the password on connections from another host or container. The June note, however, wrote ai_password directly into the Compose file and into DATABASE_URL in .env.example

“Open on every interface” does not by itself mean open to the internet. Whether another machine can connect to the port depends on the host's network and firewall. The docs give these conditions.

  • Docker Engine (Linux): the port publishing docs say “Publishing container ports is insecure by default” and explain that a published port becomes reachable not only from the Docker host but from the outside world. According to the firewall docs, Docker's forwarding rules apply before Ubuntu's ufw rules, so a port you thought ufw was blocking may still be open.
  • Docker Desktop (macOS, Windows): the networking docs say docker run -p listens on all interfaces (0.0.0.0) by default. You can change the default to localhost in Docker Desktop's network settings, and host firewalls can filter inbound connections by com.docker.backend.
  • A caveat from Qdrant's docs: a local development machine likely has a firewall that blocks public access, but a public VPS or dedicated server may not. Copying the same Compose file to a cloud server raises the risk.

A Compose example that opens ports on 127.0.0.1 only

This keeps the June note's setup and changes only what the docs recommend. Secrets are read from a .env file in the same folder; only the Redis password lives in a config file (./redis/redis.conf). We did not run this stack while preparing the article, but each line follows the format in the relevant official docs.

services:
  ollama:
    image: ollama/ollama
    ports:
      - "127.0.0.1:11434:11434"
    volumes:
      - ollama:/root/.ollama

  qdrant:
    image: qdrant/qdrant
    ports:
      - "127.0.0.1:6333:6333"
      - "127.0.0.1:6334:6334"
    environment:
      QDRANT__SERVICE__API_KEY: ${QDRANT_API_KEY}
    volumes:
      - qdrant:/qdrant/storage

  postgres:
    image: postgres:18
    ports:
      - "127.0.0.1:5432:5432"
    environment:
      POSTGRES_USER: ai
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: aidb
    volumes:
      - postgres:/var/lib/postgresql

  redis:
    image: redis:8
    command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
    ports:
      - "127.0.0.1:6379:6379"
    volumes:
      - ./redis:/usr/local/etc/redis

volumes:
  ollama:
  qdrant:
  postgres:
# .env  (keep out of git)
QDRANT_API_KEY=a_long_random_string
POSTGRES_PASSWORD=a_long_random_string
# ./redis/redis.conf  (keep out of git)
requirepass a_long_random_string
  • Binding to 127.0.0.1: Docker's docs say that adding 127.0.0.1 (or ::1) to the publish flag means only the Docker host can reach the port. They also warn that in Docker Engine releases older than 28.0.0, other machines on the same local network segment (the same layer-2 network, for example machines on the same switch with no router between them) could still reach ports published to localhost. Check your Engine version with docker version first.
  • Ports used only between containers do not need publishing. According to the Compose networking docs, services in the same Compose file reach each other on the default network by service name (qdrant:6333). The host port is used only for access from outside the Compose default network. If your app runs on the host with uv run, publish to 127.0.0.1 as above.
  • Qdrant API key: once QDRANT__SERVICE__API_KEY is set, every request needs an api-key header. Clients that only query can get a separate read-only key (QDRANT__SERVICE__READ_ONLY_API_KEY, since 1.7.0). Qdrant's docs say to use TLS as well, because a key sent over an unencrypted connection can leak. If you plan to open Qdrant beyond the host, you need TLS.
  • Redis password: the official image docs recommend setting a password through a config file if you plan to expose Redis, and describe mounting a folder containing redis.conf at /usr/local/etc/redis and passing it to redis-server. As in the example, put the password on a requirepass line in ./redis/redis.conf and keep that file out of git like .env. Without that line, Redis accepts connections without a password even on 127.0.0.1.
  • Volume path for PostgreSQL 18: starting with 18, the official image changed PGDATA to /var/lib/postgresql/18/docker and declares its volume at /var/lib/postgresql. If you use postgres:16 as the June note did, mount at the old location (/var/lib/postgresql/data) or the data will not persist.
  • Ollama on macOS: Ollama's macOS docs say Apple M-series machines get both CPU and GPU support, while its Docker docs only cover CPU-only runs and NVIDIA and AMD GPUs. On macOS, installing the app fits the docs better. The app binds to 127.0.0.1, so you can drop the ollama service from the Compose file.

After starting the stack, check that the PORTS column of docker compose ps starts with the loopback address, as in 127.0.0.1:6333->6333/tcp. Any line starting with 0.0.0.0: is a port open on every interface.

How a coding agent on the same machine reaches these services

Binding to 127.0.0.1 blocks other machines, but a coding agent's tests and scripts still run locally and can reach those services. The two agents have different defaults (official docs, checked 2026-10-06).

ItemClaude CodeCodex CLI
Shell command sandbox by defaultOff. Turn it on with /sandbox or sandbox.enabled: trueOn. Commands run in an OS-enforced sandbox
Writes inside the sandboxThe working directory, a per-user temp directory, and directories you have addedLimited to the active workspace
Reads inside the sandboxMost of the machine. The docs say credential files such as ~/.ssh and ~/.aws/credentials are readable by defaultNot checked for this article
Network inside the sandboxNo direct route out; traffic goes through a proxy that checks an allowed-domain list, which starts emptyCommand network access is off by default
Access to localhost servicesOn macOS you must turn on network.allowLocalBinding: true, which then allows connections to any port on localhost. On Linux and WSL2, a sandboxed command's localhost is separate from the host'sWith network turned on and network_proxy enabled, the default allow_local_binding = false blocks loopback and private addresses. To let a command reach one local target, add an allow rule for that exact local IP or for localhost
When the sandbox cannot startRuns commands without the sandbox by default. sandbox.failIfUnavailable: true makes it exit at startup insteadNot checked for this article
An unauthenticated local service can become a way around the sandbox. Claude Code's sandbox docs warn that once allowLocalBinding is on in macOS, “a localhost service that doesn't require authentication … can then act for the command outside the sandbox.” The docs' example is a debugger. Ollama, Qdrant and Redis without authentication carry the same risk: a command inside the sandbox could delete a Qdrant collection or flush Redis. If you plan to turn this setting on, set the Qdrant API key and Redis password first.

One change in the Codex docs as of 2026-10-06: approval_policy = "untrusted" has been retired, and if the value is still in a config file, Codex may fail to start. The docs say you can keep the stricter per-command approval behavior by adding trust_level = "untrusted" to a project entry in ~/.codex/config.toml instead.

Defaults to check when adding MCP servers

Claude Code's MCP scopes and approval

ScopeLoads inShared with teamStored in
local (default)Current project onlyNo~/.claude.json
projectCurrent project onlyYes, through version control.mcp.json at the repository root
userAll your projectsNo~/.claude.json

Claude Code MCP docs, checked 2026-10-06. The latest version at that time was 2.1.290 (2026-10-05).

  • The approval prompt appears only in interactive sessions. The docs say Claude Code asks for approval in interactive sessions before using project-scoped servers from .mcp.json. In claude -p runs, Agent SDK sessions, and cloud sessions, Claude Code cannot show that prompt, so it loads those servers without asking. If CI or automation works on repositories you do not control, block servers with disabledMcpjsonServers, or name the only servers to use with --strict-mcp-config and --mcp-config.
  • A repository cannot approve its own servers. Since 2.1.196, until you accept the workspace trust dialog, Claude Code ignores enableAllProjectMcpServers and enabledMcpjsonServers committed in the repository's .claude/settings.json.
  • Keep API keys out of .mcp.json. .mcp.json substitutes environment variable values for ${VAR} and ${VAR:-default} in command, args, env, url and headers. Write, for example, "Authorization": "Bearer ${API_KEY}" and keep the value in each person's environment.

MCP tool definitions: the field name in the June note's example

The June note's MCP tool example used input_schema. The MCP specification's field name is inputSchema. input_schema belongs to the tool format of Anthropic's Messages API and does not appear in the tools page of the MCP specification (2026-07-28 revision). In the specification's format, the example becomes:

{
  "name": "search_documents",
  "description": "Searches a document collection for relevant evidence. Does not delete or modify anything.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "query": {"type": "string"},
      "collection": {"type": "string"},
      "top_k": {"type": "integer", "minimum": 1, "maximum": 20}
    },
    "required": ["query", "collection"]
  },
  "annotations": {"readOnlyHint": true}
}
  • Annotations (annotations) are optional and only hints. A tool definition requires inputSchema, but annotations are optional. In the specification's schema, readOnlyHint defaults to false, and when a tool is not read-only, destructiveHint defaults to true. The specification says clients MUST treat annotations as untrusted unless they come from trusted servers.
  • Codex uses these annotations in approval decisions. According to the Codex docs, MCP tool calls always require approval when the tool advertises a destructive annotation. If it also advertises a read annotation, the read annotation takes priority.
  • Human confirmation is the specification's recommended default. The specification's tools page says there SHOULD always be a human in the loop with the ability to deny tool invocations. Splitting read tools and write tools into separate servers or separate keys lets you narrow permissions on the server side as well, as with Qdrant's read-only key above.

What changed from the June note

June noteThis versionWhy
A “first choice” tool table per area (LangGraph, Qdrant, FastAPI, Next.js and others)RemovedNo comparison or measurement backed the ranking
Recommended RAM of 16 GB, 32 GB and 64 GBRemovedNo source was given, and the figure depends on the model and quantization
Compose example for Ollama, Qdrant, PostgreSQL and RedisPorts bound to 127.0.0.1; secrets moved to .env and redis.confThe official docs in “Ports exposed by the June note's Compose example”
input_schema in the MCP tool schema exampleChanged to inputSchema, with an annotation example addedMCP specification, 2026-07-28 revision
Homebrew, mise, uv and pnpm install commands, a 7-day roadmap, PRD and prompt templatesRemovedThese were the author's working habits rather than verifiable facts, and versions change quickly

Measurements of how MCP servers behave in practice are in our Korean article on giving agents only bash, and how agent sandboxes isolate work is in The three layers of a coding agent sandbox.

Checklist

  1. Look at the PORTS column of docker compose ps. If a line starts with 0.0.0.0:, prefix that entry under ports: in the Compose file with 127.0.0.1:.
  2. Make sure Docker Engine is 28.0.0 or later. In earlier releases, other machines on the same local network segment could reach ports published to localhost.
  3. Set an API key on Qdrant and a password on Redis. Binding to 127.0.0.1 still lets processes and coding agents on the same machine reach them. Give apps and MCP servers that only query a Qdrant read-only key.
  4. Do not put passwords in the Compose file or .env.example. Read them from .env as with ${POSTGRES_PASSWORD}, and keep .env out of git.
  5. If you enable Claude Code's sandbox, review localhost access (allowLocalBinding) and credential-file read access at the same time. The default read scope includes files such as ~/.ssh. If the sandbox is mandatory, turn on failIfUnavailable.
  6. Read a repository's .mcp.json before approving it. Automation (claude -p, the Agent SDK) loads it without a prompt, so set the scope with --strict-mcp-config or disabledMcpjsonServers.
  7. Search your Codex config files for approval_policy = "untrusted" and remove it. The value has been retired, and Codex may fail to start.
  8. Define your own MCP tools with inputSchema and state their behavior with annotations. Annotations are optional, but marking query tools readOnlyHint: true and leaving destructiveHint at its default (true) for tools that delete or modify lets clients such as Codex use them in approval decisions.

What is not settled yet

  • This article is a 2026-10-06 rewrite, to current standards, of the initial note first published on 2026-06-16. The June note collected tool recommendations and install commands without sources; this version keeps only defaults that can be confirmed in official docs.
  • We did not measure anything ourselves. We did not run the Compose example, connect to the ports from another device, or test localhost access from an agent sandbox. Every behavior is reported as the official docs describe it.
  • “Open on every interface” is not the same as actual exposure. What can actually connect depends on your router, corporate network, cloud security groups and host firewall. This article covers only the defaults each project documents.
  • The advice to install Ollama as an app on macOS is an inference from comparing the docs. Ollama's docs do not say outright that Docker cannot use the Apple GPU; we only confirmed that its Docker docs give no instructions for running on the Apple GPU.
  • We did not check Codex's sandbox read scope or what Codex does when the sandbox cannot start. The empty cells in the table are the ones we did not check.
  • Versions and setting keys change quickly. Claude Code ships a new version almost every day, and at the time of checking the Codex docs had moved to learn.chatgpt.com. Changes after 2026-10-06 are not reflected.

References

All documents were opened and compared against the original text on 2026-10-06. Where possible, we fetched the Markdown source or the documentation source on GitHub.

SourceWhat this article took from itType
Docker, Port publishing and mapping“insecure by default”, binding to 127.0.0.1, the L2 warning for releases before 28.0.0Primary
Docker, Packet filtering and firewallsDocker's rules applying before ufwPrimary
Docker Desktop, NetworkingDefault 0.0.0.0 binding for -p, changing the default, filtering by com.docker.backendPrimary
Compose file reference, services · Networking in Compose0.0.0.0 when host_ip is unset, the 127.0.0.1: format, access by service namePrimary
Ollama docs (faq, api/authentication, docker, macos) · DockerfileNo authentication on the local API, default 127.0.0.1 binding, the image's OLLAMA_HOST=0.0.0.0:11434, GPU support. Latest release v0.35.1 (2026-09-29)Primary (developer)
Qdrant, Security · QuickstartNo authentication by default, binding to all interfaces, API and read-only keys, TLS recommendation, ports 6333 and 6334. Latest release v1.19.2 (2026-10-05)Primary (developer)
Official Redis image docs · Redis security · Official PostgreSQL image docsRedis protected mode turned off and config-file mounting, requirepass, PostgreSQL's password requirement and the PGDATA change in 18Primary
Claude Code, MCP · Sandboxing · ChangelogMCP scopes, approval, workspace trust, environment variable substitution; sandbox defaults and the localhost warning; latest version 2.1.290Primary (developer)
Codex, Agent approvals & securitySandbox on and network off by default, the retired untrusted policy, allow_local_binding, approval for destructive annotationsPrimary (developer)
MCP specification 2026-07-28, Tools · schema.tsThe inputSchema field, annotation defaults and trust conditions, the human-in-the-loop recommendationPrimary
Claude docs, Tool use overviewThe input_schema field in Messages API tool definitionsPrimary (developer)

We rewrote the 2026-06-16 initial note to current standards on 2026-10-06. The first version's tool recommendation table, hardware recommendations and install commands were removed for lack of evidence, and the Compose and MCP tool examples were corrected to match the official docs. If the docs change, we will update this article and note the revision date.

Newsletter

The week’s new reports, every Sunday

One email a week with that week’s reports, skipped when nothing new came out. Nothing but report news.

Used only to announce new reports. Unsubscribe any time. Privacy Policy
Publisher

Eazler AI Lab is the AI engineering journal of Eazler Inc. We read primary sources in full, and say so when something is unverified.

Editorial standards and method →

We also post report summaries on Threads: @eazler.inc.

Building or adopting AI agents? hello@eazler.com