Security Best Practices for Running the MCP-PostgreSQL-Ops Server in Production

Run the MCP-PostgreSQL-Ops server with a dedicated low-privilege database user, enforce TLS connections via sslmode=verify-full, enable Bearer-token authentication using the --auth-enable flag, and isolate the container network to ensure a hardened production deployment.

Deploying the MCP-PostgreSQL-Ops server in a production environment requires a defense-in-depth strategy that secures the database connection, the server process, and the surrounding infrastructure. According to the repository's source code, the server includes built-in safety mechanisms such as automatic password masking and non-root container execution, but production deployments must harden these defaults further. This guide covers ten critical security best practices derived directly from the SECURITY.md, Dockerfile.MCP-Server, and core Python modules.

1. Enforce the Principle of Least Privilege on the Database

The MCP server only needs to query system catalogs and statistics views. It does not require superuser privileges or write access to application data.

Create a Dedicated Read-Only Role

Create a role that has only CONNECT permission and SELECT rights on specific system catalogs used by the tools in src/mcp_postgresql_ops/functions.py. Grant pg_read_all_stats only if you need to query pg_stat_statements or pg_stat_monitor.

CREATE ROLE mcp_user LOGIN PASSWORD '••••••••';
GRANT CONNECT ON DATABASE ecommerce TO mcp_user;
GRANT USAGE ON SCHEMA public TO mcp_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO mcp_user;
GRANT SELECT ON pg_catalog.pg_stat_activity TO mcp_user;
GRANT SELECT ON pg_catalog.pg_settings TO mcp_user;
GRANT SELECT ON pg_catalog.pg_user TO mcp_user;
GRANT pg_read_all_stats TO mcp_user;  -- Optional: needed for pg_stat_* extensions

Never grant SUPERUSER or CREATEDB attributes to this account. The Security Best Practices section in SECURITY.md explicitly recommends this isolation to prevent privilege escalation.

2. Secure Database Connections with TLS

Always encrypt traffic between the MCP server and PostgreSQL. Configure the server to verify the database identity and reject unencrypted connections.

  • Set ssl = on in postgresql.conf on the database server.
  • Use sslmode=verify-full in the connection string to ensure certificate validation.
  • Configure the client certificate authority to prevent man-in-the-middle attacks.

This ensures that credentials and query results cannot be intercepted on the network.

3. Protect Credentials Using Docker Secrets

Never commit secrets to version control. The repository provides a .env.example template that you must copy to a real .env file excluded from Git.

For production, migrate from environment variables to Docker secrets or an external secrets manager like HashiCorp Vault or AWS Secrets Manager.

services:
  mcp-server:
    image: call518/mcp-server-postgresql-ops:latest
    env_file: .env
    secrets:
      - POSTGRES_PASSWORD
      - REMOTE_SECRET_KEY
    command: >
      uvx --python 3.12 mcp-postgresql-ops
      --type streamable-http
      --host 0.0.0.0
      --port 8000
      --auth-enable
      --secret-key $REMOTE_SECRET_KEY

secrets:
  POSTGRES_PASSWORD:
    file: ./secrets/postgres_password.txt
  REMOTE_SECRET_KEY:
    file: ./secrets/remote_secret_key.txt

This approach keeps sensitive material out of environment variables and process listings.

4. Enable Bearer-Token Authentication

The HTTP transport must never run unauthenticated in production. In src/mcp_postgresql_ops/mcp_main.py, the server supports the --auth-enable flag to enforce Bearer-token validation.

Configure your .env with a strong random key:

REMOTE_AUTH_ENABLE=true
REMOTE_SECRET_KEY=3b2c5f8e9a1d4f6b7c9e0a1b2c3d4e5f6g7h8i9j0k

Use at least 32 cryptographically random characters. The Security & Authentication section in README.md warns that running with REMOTE_AUTH_ENABLE=false exposes the database metadata to any network client.

5. Isolate the Container Network

The Dockerfile.MCP-Server already configures the container to run as a non-root appuser. Build upon this with network-level controls:

  • Restrict inbound traffic to port 8000 to trusted subnets only.
  • Deploy security groups or firewalls that block external access to the PostgreSQL port (5432).
  • Allow only the MCP container or trusted bastion hosts to reach the database.

This layering ensures that even if the application layer is compromised, the attacker cannot pivot directly to the database host.

6. Implement HTTPS with a Reverse Proxy

Terminate TLS at a reverse proxy (Nginx or Traefik) placed in front of the MCP server. The proxy should:

  • Handle TLS certificate management (Let's Encrypt or corporate PKI).
  • Verify the Authorization Bearer token header.
  • Forward only validated requests to localhost:8000 inside the container network.

Never expose the plain HTTP port 8000 directly to the public internet.

7. Mask Sensitive Data in Logs

The repository includes a sanitize_connection_info function in src/mcp_postgresql_ops/functions.py that automatically redacts passwords from log output. Ensure this behavior is active by verifying that connection string logging is enabled and that the sanitization filter is not disabled.

Forward container logs to a centralized system (ELK, CloudWatch, or Splunk) and configure alerts for authentication failures or requests from unexpected IP addresses.

8. Keep Dependencies Updated

Security patches for PostgreSQL and Python dependencies must be applied promptly. The repository includes .github/dependabot.yml to automate dependency updates. Enable this to receive pull requests for security advisories affecting the server.

  • Upgrade PostgreSQL to the latest supported major version (12–17).
  • Rebuild the container image monthly to incorporate OS-level security patches.

9. Harden Container Images

The official images use the Percona PostgreSQL base as defined in Dockerfile.MCP-Server and Dockerfile.MCPO-Proxy. Before deployment:

  • Scan images with Trivy, Docker Bench, or Grype to detect CVEs.
  • Remove unnecessary packages and ensure the appuser has no sudo privileges.
  • Pin image digests rather than using floating tags like latest.

10. Audit PostgreSQL Extensions

Extensions like pg_stat_statements and pg_stat_monitor expose detailed query fingerprints. Enable them only when required by specific tools, as detailed in the Extension-Dependent Tools matrix in README.md. When disabled, remove them from shared_preload_libraries to prevent information leakage via the statistics views.

Summary

  • Database: Use a dedicated role with pg_read_all_stats only, never superuser.
  • Transport: Enforce sslmode=verify-full and terminate HTTPS at a reverse proxy.
  • Authentication: Enable --auth-enable with a strong REMOTE_SECRET_KEY.
  • Secrets: Store credentials in Docker secrets or a vault, never in Git.
  • Runtime: Run as non-root appuser, restrict port 8000, and mask logs via sanitize_connection_info.
  • Maintenance: Enable Dependabot (.github/dependabot.yml) and scan images before deployment.

Frequently Asked Questions

Does MCP-PostgreSQL-Ops require superuser access to PostgreSQL?

No. The server only queries system catalogs and statistics views. Create a dedicated role with CONNECT, USAGE, and SELECT permissions on pg_catalog tables. Grant pg_read_all_stats only if you need extension-dependent tools like pg_stat_statements. Superuser rights violate the principle of least privilege and are unnecessary for the tools implemented in src/mcp_postgresql_ops/functions.py.

How do I enable authentication for the MCP server HTTP API?

Pass the --auth-enable flag when starting the server, as implemented in src/mcp_postgresql_ops/mcp_main.py. Set REMOTE_AUTH_ENABLE=true and REMOTE_SECRET_KEY to a random 32-character string in your environment. Production deployments must not run with authentication disabled, as warned in SECURITY.md.

What is the safest way to manage database credentials in production?

Use Docker secrets or an external secrets manager rather than environment variables in your .env file. The repository provides a .env.example template that you should copy and exclude from version control. Reference the Docker Deployment Security subsection in SECURITY.md for the Docker Compose syntax using the secrets: top-level element.

How does the server protect passwords in log files?

The sanitize_connection_info function in src/mcp_postgresql_ops/functions.py automatically parses connection strings and replaces password values with [REDACTED] before logging. This prevents accidental credential exposure in container logs or centralized logging systems like ELK or CloudWatch.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →