> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ardor.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Networking

> Connect your services together, configure ports, and control public vs private access.

<img src="https://mintcdn.com/ardor/sf-RwXk0pWSNetwj/docs/services/images/network.webp?fit=max&auto=format&n=sf-RwXk0pWSNetwj&q=85&s=bc30549254fd35e0b57cc5ec9bef83cb" alt="Service Networking Configuration" width="1022" height="1069" data-path="docs/services/images/network.webp" />

## Overview

Every service in Ardor gets network connectivity out of the box. Services can talk to each other over the internal network, and you can expose them publicly when needed.

<Tip>
  **Cerebrum configures networking.** Ask "connect my API to the database" or "make this service public" — Cerebrum will set up the connections and environment variables. The details below explain how it works.
</Tip>

<CardGroup cols="2">
  <Card title="Internal Network" icon="lock">
    Services communicate privately using internal URLs. Fast, secure, no internet roundtrip.
  </Card>

  <Card title="Public Access" icon="globe">
    Expose services to the internet with a public URL for APIs, websites, and webhooks.
  </Card>
</CardGroup>

## How Services Connect

Each service gets two URLs:

| URL Type     | Use For                          |
| ------------ | -------------------------------- |
| **Internal** | Service-to-service communication |
| **Public**   | Browser access, external clients |

### Cross-Service Communication

Backend services can reach any other service by its internal URL:

```mermaid theme={null}
flowchart LR
    API[API Backend] -->|internal| DB[(PostgreSQL)]
    API -->|internal| Cache[(Redis)]
    Worker[Background Worker] -->|internal| DB
    Worker -->|internal| Queue[(RabbitMQ)]
```

<Warning>
  **Browser can't use internal URLs.** If your frontend runs in the browser (React, Vue, etc.), it's outside Ardor's network. Set up a reverse proxy in your frontend service to route API requests internally — this is the recommended pattern.
</Warning>

## Public vs Private Services

When configuring a service, you choose its visibility:

<Tabs>
  <Tab title="Public">
    **When to use:**

    * Web applications users visit
    * APIs external clients call
    * Webhook endpoints

    **What happens:**

    * Service gets a public URL: `https://<port>-<uuid>.ardor.cloud`
    * Anyone on the internet can access it
    * Still accessible internally too
  </Tab>

  <Tab title="Private">
    **When to use:**

    * Databases (PostgreSQL, MongoDB, Redis)
    * Internal microservices
    * Background workers
    * Anything that shouldn't be exposed

    **What happens:**

    * No public URL
    * Only reachable via internal URL
    * More secure — no attack surface from internet
  </Tab>
</Tabs>

<Warning>
  **Never expose databases publicly.** Always keep them private and connect from your backend services using internal URLs.
</Warning>

## Bind Custom Domain

<img src="https://mintcdn.com/ardor/sf-RwXk0pWSNetwj/docs/services/images/bind-domain-to-service.webp?fit=max&auto=format&n=sf-RwXk0pWSNetwj&q=85&s=fc520cd256756ae182992efb68329a37" alt="Service Domain Binding" width="1275" height="763" data-path="docs/services/images/bind-domain-to-service.webp" />

You can bind a [custom domain](/docs/custom-domains/custom-domain) to a service in a specific environment. The service must be **Public** for that.

<Steps>
  <Step title="Choose the Environment">
    Select the environment where the domain should route traffic, such as development, staging, or production. Domain bindings are environment-specific.
  </Step>

  <Step title="Select a Public Service">
    Pick the service that should receive traffic for the domain. Private services cannot be selected as custom domain targets.
  </Step>

  <Step title="Deploy the Environment">
    Deploy the environment after saving the binding so the updated networking configuration is applied at runtime.
  </Step>
</Steps>

After you select a [custom domain](/docs/custom-domains/custom-domain), Ardor updates the relevant [system variables](/docs/services/variables-and-secrets#system-variables) for that service.

<Note>
  Changing or removing a [custom domain](/docs/custom-domains/custom-domain) follows the same rule: save the change, then deploy the environment so services receive the updated [system variables](/docs/services/variables-and-secrets#system-variables) and routing configuration.
</Note>

## Port Configuration

Each service listens on a specific port. Ardor passes this as the `PORT` environment variable.

### How Ports Work

1. You configure the port in service settings (default: 8080)
2. Ardor injects it as `PORT` environment variable
3. Your app reads `PORT` and listens on it
4. Both internal and public URLs route to this port

### Reading the Port

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import os
    port = int(os.environ.get('PORT', 8080))
    app.run(host='0.0.0.0', port=port)
    ```
  </Tab>

  <Tab title="Node.js">
    ```javascript theme={null}
    const port = process.env.PORT || 8080;
    app.listen(port, '0.0.0.0');
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
    http.ListenAndServe("0.0.0.0:"+port, nil)
    ```
  </Tab>
</Tabs>

<Warning>
  Always bind to `0.0.0.0`, not `localhost` or `127.0.0.1`. Otherwise your service won't be reachable from outside the container.
</Warning>

### Port in Internal URL

The port is included in the internal URL you get from service settings. Use it directly — no need to specify the port separately.

## Common Patterns

### API + Database

```mermaid theme={null}
flowchart LR
    Internet((Internet)) --> API[API Backend<br/>Public]
    API -->|internal URL| DB[(PostgreSQL<br/>Private)]
```

**Setup:**

1. PostgreSQL service — **Private**
2. API service — **Public**, connects via internal URL stored in env var

### Frontend + Backend + Database (Recommended)

```mermaid theme={null}
flowchart LR
    Browser((Browser)) -->|public URL| FE[Frontend + Proxy<br/>Public]
    FE -->|internal URL| API[API Backend<br/>Private]
    API -->|internal URL| DB[(PostgreSQL<br/>Private)]
    API -->|internal URL| Cache[(Redis<br/>Private)]
```

**Setup:**

1. PostgreSQL — **Private**
2. Redis — **Private**
3. API Backend — **Private**, only reachable via internal URL
4. Frontend — **Public** with reverse proxy, routes `/api/*` to backend internally

<Tip>
  **Best practice:** Keep your API backend private and route requests through your frontend's reverse proxy. This way only one service is exposed to the internet.
</Tip>

### Frontend + Public API (when needed)

If you intentionally need a public API (for external clients, mobile apps, webhooks):

```mermaid theme={null}
flowchart LR
    Browser((Browser)) -->|public URL| FE[Frontend<br/>Public]
    Browser -->|public URL| API[API Backend<br/>Public]
    External[External Clients] -->|public URL| API
    API -->|internal URL| DB[(PostgreSQL<br/>Private)]
```

**Use this when:**

* External apps need to call your API directly
* You're building a public API for third parties
* Mobile apps need direct backend access

### Microservices

```mermaid theme={null}
flowchart LR
    Internet((Internet)) --> Gateway[API Gateway<br/>Public]
    Gateway -->|internal| Users[Users Service<br/>Private]
    Gateway -->|internal| Orders[Orders Service<br/>Private]
    Users -->|internal| DB[(Database<br/>Private)]
    Orders -->|internal| DB
```

**Setup:**

* Only the API Gateway is public
* All microservices are private, communicate via internal URLs

## Troubleshooting

<AccordionGroup>
  <Accordion title="Can't connect to another service">
    **Check:**

    * Is the target service running? Check its status and logs
    * Are you using the correct internal URL and port?
    * Are both services in the same environment?

    **Ask Cerebrum** to help diagnose connection issues.
  </Accordion>

  <Accordion title="Connection refused errors">
    **Cause:** Target service isn't listening on the expected port.

    **Check:**

    * Is the service reading the `PORT` environment variable?
    * Is it binding to `0.0.0.0`?
    * Check the service logs for startup errors
  </Accordion>

  <Accordion title="Public URL not working">
    **Check:**

    * Is the service set to **Public** visibility?
    * Is the service running and healthy?
    * Is it listening on the configured port?
  </Accordion>

  <Accordion title="Timeout connecting to database">
    **Check:**

    * Is the database service running?
    * Are you using the internal URL (not trying to connect externally)?
    * Check database logs for connection limit issues
  </Accordion>
</AccordionGroup>

## Best Practices

<AccordionGroup>
  <Accordion title="Keep databases private">
    Never expose databases to the internet. Always use private visibility and connect via internal URLs.
  </Accordion>

  <Accordion title="Keep API backends private when possible">
    Unless you need a public API for external clients, keep your backend private and route requests through your frontend's reverse proxy. One public endpoint = smaller attack surface.
  </Accordion>

  <Accordion title="Use environment variables for URLs">
    Don't hardcode internal URLs. Store them in variables so you can change them without code changes.

    ```python theme={null}
    DATABASE_HOST = os.environ['DATABASE_HOST']
    ```
  </Accordion>

  <Accordion title="Minimize public services">
    Only expose what needs to be public. Fewer public endpoints = smaller attack surface.
  </Accordion>

  <Accordion title="Add authentication to sensitive services">
    Add authentication for extra security on services that handle sensitive data.
  </Accordion>
</AccordionGroup>

## What's Next

<CardGroup cols="2">
  <Card title="Variables & Secrets" icon="key" href="/docs/services/variables-and-secrets">
    Store connection strings and credentials securely
  </Card>

  <Card title="Docker Images" icon="docker" href="/docs/services/create_service/docker-image">
    Deploy databases and caches from Docker Hub
  </Card>

  <Card title="Deployments" icon="rocket" href="/docs/environments/deployments">
    Deploy your connected services to production
  </Card>

  <Card title="Logging" icon="scroll" href="/docs/services/logging">
    Debug connection issues with logs
  </Card>
</CardGroup>
