This guide shows you how to expose TCP services (databases, message queues, custom protocols) with automatic TLS encryption through the gateway.

How It Works

When you expose a TCP port, the gateway automatically wraps your traffic in TLS:
  1. Client connects via TLS to <app-id>-<port>.<cluster>.phala.network
  2. Gateway terminates TLS and decrypts the traffic
  3. Gateway forwards plain TCP to your service inside the CVM
  4. Your service doesn’t need to handle TLS - the gateway does it for you
This gives you encrypted connections without configuring certificates in your service.

Example: PostgreSQL Database

services:
  postgres:
    image: postgres:15
    ports:
      - "5432:5432"  # Expose PostgreSQL port
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: myapp
      POSTGRES_USER: dbuser
    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:
After deployment, connect to your database:
# Direct TLS connection (if client supports it)
psql "postgresql://dbuser:password@<app-id>-5432.dstack-prod5.phala.network:443/myapp?sslmode=require"

Example: Custom TCP Service

services:
  tcp-server:
    image: your-tcp-service:latest
    ports:
      - "9000:9000"  # Your custom port
    environment:
      # No TLS configuration needed - gateway handles it
      PORT: 9000

Connecting with TLS-to-TCP Conversion

Many existing clients don’t support connecting to TLS-wrapped TCP services. Use these methods to convert TLS back to plain TCP on your local machine:

Port Forwarding Methods

# Forward local port 5432 to PostgreSQL
socat TCP-LISTEN:5432,bind=127.0.0.1,fork,reuseaddr \
      OPENSSL:<app-id>-5432.dstack-prod5.phala.network:443

# Now connect normally
psql -h 127.0.0.1 -p 5432 -U dbuser myapp

# Forward local port 9000 to custom service
socat TCP-LISTEN:9000,bind=127.0.0.1,fork,reuseaddr \
      OPENSSL:<app-id>-9000.dstack-prod5.phala.network:443

# Connect with any TCP client
telnet 127.0.0.1 9000

Benefits

  • Automatic encryption: No need to configure TLS in your service
  • Zero-trust security: All external connections are encrypted
  • Client compatibility: Use TLS-to-TCP conversion for legacy clients
  • Simple deployment: Just expose the port, TLS is automatic

Internal vs External Access

Services can communicate internally without TLS:
services:
  app:
    image: my-app:latest
    environment:
      # Internal: plain connection
      DB_HOST: postgres
      DB_PORT: 5432
      
  postgres:
    image: postgres:15
    ports:
      - "5432:5432"  # External: TLS-wrapped by gateway
External clients use TLS, internal services use plain connections for better performance.

Troubleshooting

Connection refused?
  • Verify the port is exposed in docker-compose.yml
  • Check the service is running: SSH in and run docker ps
TLS handshake failed?
  • Ensure you’re connecting to port 443 (not the service port)
  • Use the correct hostname for TLS verification
socat not found?
  • Install it: apt install socat (Ubuntu) or brew install socat (macOS)
  • Or use the Python script alternative

Next Steps