# Step-by-Step Guide: How to Deploy a Self-Hosted Lampline Voice Agent and Import from Dialogflow CX

> AI Release · @ai_release1 · https://ai-release.org/guides/poshagovoe-rukovodstvo-kak-razvernut-self-hosted-goloso_en.html

## TL;DR

- **Time to complete:** 60–90 minutes (30 of them are waiting for DNS and TLS)
- **Difficulty:** Intermediate — you need to be comfortable with SSH, Docker Compose and the Google Cloud console
- **You will need:**
  - A Linux host (Ubuntu 22.04 LTS or similar) with 2 vCPU / 4 GB RAM / 20 GB free disk as a starting point
  - Docker Engine + the Compose plugin
  - A domain or subdomain with an A record pointing to the host, e.g. `voice.example.com`
  - A Dialogflow CX agent and permission to export it (`dialogflow.agents.export`)
  - API keys for the LLM, STT (speech-to-text) and TTS (text-to-speech) providers your Lampline build supports
- **Important:** the Lampline image name, tag, default port, environment-variable names and CLI subcommands are build-specific. Every such value below is a placeholder marked **(check the documentation)** — take the real value from your Lampline release notes and `lampline --help` output. Everything about Dialogflow CX export is standard Google Cloud behaviour.

## Requirements and setup
![Illustration: Requirements and setup](https://ai-release.org/guides/img/poshagovoe-rukovodstvo-kak-razvernut-self-hosted-goloso-en-1.jpg)


- **OS:** Ubuntu 22.04 LTS (or any distro with Docker support). Log in as a user in the `docker` group.
- **Docker:** Engine 24+ and Compose v2 (`docker compose`, not `docker-compose`).
- **Ports:** 80 and 443 open in the firewall; the app port (8080 in the examples) reachable only through the reverse proxy.
- **DNS:** `A voice.example.com -> <your-server-ip>`, already propagated.
- **TLS:** a certificate for that hostname. Caddy or Nginx + certbot both work; the examples use Caddy because it obtains certificates automatically.
- **Google Cloud:** a project containing your Dialogflow CX agent; your account needs a role that includes `dialogflow.agents.export` (for example a Dialogflow CX Agent Editor role — confirm the exact role in the IAM docs).
- **Disk:** at least 20 GB free — voice containers plus models and call recordings grow fast.

Check the basics before installing anything:

```bash
docker --version && docker compose version
df -h /opt
dig +short voice.example.com
```

**Example:** `dig` must return your server IP. If it returns nothing, fix DNS first — TLS issuance will fail otherwise.

If you are deploying on a home machine instead of a VPS, the network part differs (NAT, port forwarding, dynamic IP) — see [How to add AI to your home server: self-hosted services guide](https://ai-release.org/guides/kak-podklyuchit-ii-k-domashnemu-serveru-poshagovoe-ruko.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide).

## Step 1. Prepare the host and export the Dialogflow CX agent
![Illustration: Step 1. Prepare the host and export the Dialogflow CX agent](https://ai-release.org/guides/img/poshagovoe-rukovodstvo-kak-razvernut-self-hosted-goloso-en-2.jpg)


**What to do:** create the working directory, install Docker if it is missing, then export your Dialogflow CX agent to a ZIP file.

**Why:** Lampline is a containerised service, so it needs Docker and a persistent directory. Dialogflow CX is the source of your intents, flows and training phrases — the export is the only portable artefact you can move into a self-hosted runtime.

```bash
sudo apt-get update && sudo apt-get install -y docker.io docker-compose-v2 unzip
sudo usermod -aG docker "$USER"   # log out and back in afterwards
sudo mkdir -p /opt/lampline && sudo chown "$USER" /opt/lampline
cd /opt/lampline && mkdir -p data import
```

Export the agent in the Google Cloud console: open **Dialogflow CX → your project → your agent → Agent settings (gear icon) → Export agent → Export as ZIP → Download**. The browser saves something like `agent-export.zip`.

The REST equivalent, if you prefer scripting, is the `exportAgent` method on an agent resource. The API version and the exact resource path change over time, so copy the URL from the Dialogflow CX REST reference rather than from memory:

```bash
gcloud auth print-access-token > /tmp/token
curl -X POST \
  -H "Authorization: Bearer $(cat /tmp/token)" \
  -H "Content-Type: application/json" \
  "https://dialogflow.googleapis.com/v3/projects/<project-id>/locations/<location>/agents/<agent-id>:exportAgent" \
  -d '{"agentUri":"gs://<bucket>/agent-export.zip"}'
```

**Example:** a successful call returns a long-running operation object `{"name": "projects/.../operations/..."}` — the ZIP then appears in the bucket you named.

Copy the file to the host and unpack it only for inspection:

```bash
scp agent-export.zip <user>@voice.example.com:/opt/lampline/import/
cd /opt/lampline/import && unzip -l agent-export.zip | head -20
```

**How to check:** `unzip -l` lists `agent.json`, a `flows/` directory and intent/test-case entries. If the listing is empty or the archive is 0 bytes, the download failed — repeat the console export.

| Export item | What it holds | What you will do with it |
|---|---|---|
| `agent.json` | agent-level settings: default language, time zone, speech settings | copy these values into the Lampline agent config so recognition matches |
| `flows/` | pages, routes, parameters, conditions | the flow logic you must re-create or verify after import |
| intents / training phrases | example utterances per intent | they become the utterance examples for the Lampline agent |
| webhooks | fulfillment URLs pointing at your backend | replace with URLs reachable from the Lampline host |
| test cases | regression scenarios | re-run them as scripted test calls in Step 3 |

## Step 2. Deploy Lampline and import the agent

**What to do:** write `.env` and `compose.yaml`, start the container, then run the importer against the uploaded archive.

**Why:** the container is the runtime that hosts the agent; the import converts Dialogflow CX artefacts into the internal format Lampline serves over voice.

```bash
cd /opt/lampline
cat > .env <<'EOF'
LAMPLINE_PUBLIC_URL=https://voice.example.com
LAMPLINE_ADMIN_KEY=<long-random-string>
LLM_API_KEY=<provider-key>
STT_API_KEY=<provider-key>
TTS_API_KEY=<provider-key>
EOF
chmod 600 .env
```

```yaml
services:
  lampline:
    image: ghcr.io/<vendor>/lampline:<version>   # exact image and tag: check the documentation
    restart: unless-stopped
    env_file: .env
    ports:
      - "127.0.0.1:8080:8080"    # host port : container port — both build-specific
    volumes:
      - ./data:/app/data         # persistent agents, prompts, call logs
      - ./import:/import:ro      # read-only drop zone for CX exports
```

```bash
docker compose pull && docker compose up -d
docker compose logs -f --tail=50 lampline
```

**Example:** the logs end with a line announcing the HTTP listener, e.g. `listening on 0.0.0.0:8080`. `docker compose ps` shows `Up (healthy)`.

Discover the real import command instead of guessing — every build names its subcommands differently **(check the documentation)**:

```bash
docker compose exec lampline lampline --help
docker compose exec lampline lampline agents import --help
```

Then run the import, for example:

```bash
docker compose exec lampline \
  lampline agents import --from /import/agent-export.zip --name support-cx
```

**How to check:** the import prints a summary — number of intents, flows and utterances created, plus skipped items. Confirm the agent is registered:

```bash
curl -s -H "Authorization: Bearer $LAMPLINE_ADMIN_KEY" \
  http://127.0.0.1:8080/api/agents | head
```

You should see `support-cx` in the JSON. If the response is `401`, the admin key in `.env` and the one in the request differ.

Finally, put Caddy in front so browsers get HTTPS:

```bash
sudo caddy reverse-proxy --from voice.example.com --to 127.0.0.1:8080
```

**Example:** `https://voice.example.com` then resolves with a valid certificate, and the Lampline UI or health endpoint responds instead of a browser warning.

## Step 3. Verify the agent end to end
![Illustration: Step 3. Verify the agent end to end](https://ai-release.org/guides/img/poshagovoe-rukovodstvo-kak-razvernut-self-hosted-goloso-en-3.jpg)


**What to do:** check the health endpoint, run a text turn through the agent, then place one real voice call.

**Why:** a green container does not prove the agent answers correctly — STT, the LLM, TTS and the imported flow are four separate links in one chain, and each fails differently.

```bash
curl -s https://voice.example.com/health
curl -s -X POST https://voice.example.com/api/agents/support-cx/test \
  -H "Authorization: Bearer $LAMPLINE_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text":"I want to change my delivery address"}'
```

Endpoint paths are build-specific **(check the documentation)**; the shape of the test is not — send one utterance you know is covered by an imported training phrase.

**Example:** the response contains the matched intent or flow page plus the generated reply, e.g. `{"matched":"change_address","reply":"Sure — what is the new address?"}`. A reply of `{"matched":null}` means the import skipped that intent, so re-check the import summary.

Then do one live call from a browser or a softphone to your public URL and confirm you hear the TTS voice and the transcript appears in the logs:

```bash
docker compose logs -f lampline | grep -Ei "stt|tts|turn"
```

Success checklist:

- [ ] `docker compose ps` shows `Up (healthy)`
- [ ] `/health` returns HTTP 200 over HTTPS
- [ ] `agents list` contains `support-cx`
- [ ] one test utterance returns a non-null match and a spoken reply
- [ ] the live call produces a transcript and no `ERROR` lines in the logs

Once the service is stable, add uptime alerting — [How to Set Up VPS Monitoring via a Telegram Bot with Mini App](https://ai-release.org/guides/kak-nastroit-monitoring-vps-cherez-telegram-bota-s-mini.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide) covers the bot side; point its checks at `/health`.

## Troubleshooting

**Problem. The container will not start and the port is already taken**
- Error: `Error response from daemon: driver failed programming external connectivity on endpoint lampline: bind: address already in use`
- Cause: another process (often a previously started container or a system web server) already listens on 8080 or 443.
- Solution: find and stop the owner, or change the host port:
```bash
sudo ss -ltnp | grep -E ':8080|:443'
docker compose down && docker compose up -d
```

**Problem. The export call fails with a permission error**
- Error: `PERMISSION_DENIED: Permission 'dialogflow.agents.export' denied on resource`
- Cause: the authenticated account or service account lacks the export permission on that agent or project.
- Solution: grant a role that includes `dialogflow.agents.export` in IAM, wait for propagation, then retry:
```bash
gcloud auth login && gcloud auth print-access-token >/dev/null && echo ok
```

**Problem. The voice widget loads but never connects**
- Error (browser console): `Mixed Content: The page at 'https://voice.example.com/' was loaded over HTTPS, but requested an insecure resource 'ws://...'`
- Cause: Lampline was configured with an `http://`/`ws://` public URL while the page is served over HTTPS.
- Solution: set `LAMPLINE_PUBLIC_URL=https://voice.example.com` in `.env`, restart, and confirm the generated client config uses `wss://`:
```bash
docker compose up -d && docker compose logs --tail=30 lampline
```

## Summary and next steps

You now have a self-hosted Lampline instance behind TLS with a Dialogflow CX agent imported and verified by a text turn and a live call. Next, replace the CX webhook URLs with endpoints reachable from the Lampline host and re-run your exported test cases as scripted calls.

## FAQ

**Can I import a Dialogflow CX agent without a Google Cloud service account?**
Yes, if you export the agent as a ZIP from the console with your own user account and upload that file to the host. A service account is only needed when you call the export API from a script.

**What happens to Dialogflow CX test cases and webhooks after import?**
Treat them as reference material: webhook URLs that point at Google-hosted fulfillment will not work outside CX and must be replaced, and test cases have to be re-run as scripted calls against Lampline.

**Where do imported agents live, and will they survive a container restart?**
They are written to the mounted `./data` volume. As long as you keep that volume and do not run `docker compose down -v`, a restart or image upgrade keeps your agents, prompts and logs.

**What latency should I expect from the voice agent?**
This guide does not publish a benchmark, because end-to-end latency depends on your chosen STT, LLM and TTS providers and their network distance. Measure it on your own host against the documented timing or log fields in your build.
