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

> Harden your MCP-PostgreSQL-Ops server in production with dedicated users, TLS, token auth, and network isolation. Learn essential security best practices for a secure deployment.

- Repository: [JungJungIn/mcp-postgresql-ops](https://github.com/call518/mcp-postgresql-ops)
- Tags: best-practices
- Published: 2026-02-26

---

**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`](https://github.com/call518/mcp-postgresql-ops/blob/main/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`](https://github.com/call518/mcp-postgresql-ops/blob/main/src/mcp_postgresql_ops/functions.py). Grant `pg_read_all_stats` only if you need to query `pg_stat_statements` or `pg_stat_monitor`.

```sql
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`](https://github.com/call518/mcp-postgresql-ops/blob/main/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`](https://github.com/call518/mcp-postgresql-ops/blob/main/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.

```yaml
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`](https://github.com/call518/mcp-postgresql-ops/blob/main/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:

```bash
REMOTE_AUTH_ENABLE=true
REMOTE_SECRET_KEY=3b2c5f8e9a1d4f6b7c9e0a1b2c3d4e5f6g7h8i9j0k

```

Use at least 32 cryptographically random characters. The **Security & Authentication** section in [`README.md`](https://github.com/call518/mcp-postgresql-ops/blob/main/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`](https://github.com/call518/mcp-postgresql-ops/blob/main/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`](https://github.com/call518/mcp-postgresql-ops/blob/main/.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`](https://github.com/call518/mcp-postgresql-ops/blob/main/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`](https://github.com/call518/mcp-postgresql-ops/blob/main/.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`](https://github.com/call518/mcp-postgresql-ops/blob/main/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`](https://github.com/call518/mcp-postgresql-ops/blob/main/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`](https://github.com/call518/mcp-postgresql-ops/blob/main/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`](https://github.com/call518/mcp-postgresql-ops/blob/main/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`](https://github.com/call518/mcp-postgresql-ops/blob/main/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.