---
title: Bottling line
slug: solutions/bottling-line
docTags: 
createdAt: 2026-08-17T14:39:47.729Z
---

A 500 mL PET bottling line as five machines in sequence: filler, capper, labeller, checkweigher and case packer, with the accumulation buffers between them. Six devices and 94 tags over Modbus TCP.

| Operator view      | port **4010**        |
| ------------------ | -------------------- |
| Protocol server    | port **5020**        |
| Devices and tags   | 6 devices, 94 tags   |
| Litmus Edge driver | Modbus TCP           |
| Image              | `bottling-line-demo` |

## Scenarios

- **Product giveaway.** The filler drifts high, every bottle goes out with a little more product than it was sold with, and the checkweigher sees it before anyone counts the stock. The cost is continuous rather than an event.
- **CIP verification.** A clean-in-place cycle is judged against the recipe it was supposed to follow, not against whether it finished. A wash that ran below temperature is a wash that did not happen.
- **Downtime root cause.** When the line stops, the machine that stopped is rarely the machine that caused it. The buffers between machines are what tell you which one to fix.

## What it creates on Litmus Edge

Six DeviceHub devices, one per machine plus the CIP skid, with 94 tags between them. A Digital Twin model and instance for each machine. Analytics groups behind the giveaway, CIP and downtime figures on the operator view.

## Requirements

- Litmus Edge 4.0.x, reachable over HTTPS from the host running the solution
- An API token for that Edge, or a username and password, or a client id and secret
- Docker, and Docker Compose for the compose deployment
- A network path in both directions: the solution reaches the Edge's API, and the Edge reaches this host on the protocol port

## Deploy

The solution ships two ways. Use the registry if the host can reach Google Artifact Registry, and the downloaded archive if it cannot.

### Option 1: pull from the registry

Requires a read-only registry credential, supplied by Litmus separately.

```bash
cat key.json | docker login -u _json_key --password-stdin https://us-docker.pkg.dev
```

Save this as `docker-compose.yml`:

```yaml
services:
  bottling-line:
    image: us-docker.pkg.dev/litmus-customer-facing/litmus-solutions/bottling-line-demo:0.6.1
    container_name: bottling-line-demo
    restart: unless-stopped
    ports:
      - target: 4010
        published: 4010
        protocol: tcp
      - target: 5020
        published: 5020
        protocol: tcp
    environment:
      # Fill these in.
      EDGE_URL: "${EDGE_URL:-}"
      EDGE_API_TOKEN: "${EDGE_API_TOKEN:-}"
      SIM_HOST: "${SIM_HOST:-auto}"
      APPLY_ON_START: "${APPLY_ON_START:-1}"
      EDGE_VERIFY_TLS: "${EDGE_VERIFY_TLS:-0}"
      # Optional: publish the twins into Litmus Unify.
      UNIFY_URL: "${UNIFY_URL:-}"
      UNIFY_USERNAME: "${UNIFY_USERNAME:-}"
      UNIFY_PASSWORD: "${UNIFY_PASSWORD:-}"
      UNIFY_CONNECTOR: "${UNIFY_CONNECTOR:-}"
      HTTP_PORT: "4010"
      MODBUS_PORT: "5020"
      AUTOSTART: "1"
      TICK_SECONDS: "0.2"
      SEED: "7"
      LOG_LEVEL: "INFO"
```

Put the three values in a `.env` beside the compose file:

```javascript
EDGE_URL=https://10.0.0.5
EDGE_API_TOKEN=your-token
SIM_HOST=10.0.0.9
```

```bash
docker compose up -d
```

Or, without Docker Compose, run the same image with the same `.env`:

```bash
docker run -d \
  --name bottling-line-demo \
  --restart unless-stopped \
  -p 4010:4010 \
  -p 5020:5020 \
  --env-file .env \
  -e APPLY_ON_START=1 \
  -e EDGE_VERIFY_TLS=0 \
  -e HTTP_PORT=4010 \
  -e MODBUS_PORT=5020 \
  -e AUTOSTART=1 \
  -e TICK_SECONDS=0.2 \
  -e SEED=7 \
  -e LOG_LEVEL=INFO \
  us-docker.pkg.dev/litmus-customer-facing/litmus-solutions/bottling-line-demo:0.6.1
```

Flags given with `-e` take precedence over `.env`, so change `APPLY_ON_START` or `EDGE_VERIFY_TLS` on the command line rather than in the file.

Open `http://localhost:4010`. The Edge is configured within about a minute and the page fills in.

**The page stays on its waiting panel until the Edge carries this solution's devices.** That is deliberate: the simulation does not tick and no value is shown until Litmus Edge is verified to hold the devices and poll them, so nothing on screen can be mistaken for data that never made the trip. With `APPLY_ON_START` at its default of `1` the solution configures the Edge itself on startup, which takes 30 to 60 seconds. If you set it to `0`, or the apply failed, press **Apply to Edge** in the page header.

### Option 2: load the downloaded archive

Download `bottling-line-demo-0.6.1-amd64.tar.gz` from the solution's page on portal.litmus.io. The archive is the container image, not a source bundle, and needs no registry access.

```bash
docker load -i bottling-line-demo-0.6.1-amd64.tar.gz
```

`docker load` prints the image it added:

```javascript
Loaded image: us-docker.pkg.dev/litmus-customer-facing/litmus-solutions/bottling-line-demo:0.6.1
```

That reference is the one the compose file above already names, so the same `docker-compose.yml` and `.env`, or the `docker run` command above, now work offline with no `docker login`:

```bash
docker compose up -d
```

To run it without Compose, pass the same three values on the command line:

```bash
docker run -d \
  --name bottling-line-demo \
  --restart unless-stopped \
  -p 4010:4010 \
  -p 5020:5020 \
  -e EDGE_URL=https://10.0.0.5 \
  -e EDGE_API_TOKEN=your-token \
  -e SIM_HOST=10.0.0.9 \
  us-docker.pkg.dev/litmus-customer-facing/litmus-solutions/bottling-line-demo:0.6.1
```

## Configuration

| Variable          | Default             | What it does                                                               |
| ----------------- | ------------------- | -------------------------------------------------------------------------- |
| `EDGE_URL`        | the Docker host     | Litmus Edge to configure                                                   |
| `EDGE_API_TOKEN`  | unset               | token for that Edge. Or a username and password, or a client id and secret |
| `SIM_HOST`        | `auto`              | address the Edge polls to reach this solution                              |
| `APPLY_ON_START`  | `1`                 | configure the Edge on startup                                              |
| `EDGE_VERIFY_TLS` | `0`                 | `1` checks the Edge's certificate                                          |
| `UNIFY_URL`       | unset               | optional: Litmus Unify to publish the twins into                           |
| `UNIFY_USERNAME`  | unset               | optional: a Unify user                                                     |
| `UNIFY_PASSWORD`  | unset               | optional: that user's password                                             |
| `UNIFY_CONNECTOR` | the Edge's only one | optional: which Litmus UNS connector on the Edge to publish through        |
| `AUTOSTART`       | `1`                 | start the line running                                                     |
| `TICK_SECONDS`    | `0.2`               | simulation step                                                            |
| `SEED`            | `7`                 | makes a run repeatable                                                     |

## Litmus Unify (optional)

This solution can also publish its 6 Digital Twins into Litmus Unify, one message per asset under `litmus/bottling-plant-01/...`. It stays off until two things are true: the `UNIFY_` values above are set, and the Litmus Edge has a Litmus UNS connector, which the [Solutions provisioner](solutions-provisioner) can pair in one click. The next apply then publishes the twins, the page header shows how many are arriving, and removing the solution from the Edge removes its branch from Unify as well. Leave the values empty to keep Unify out of it.

## Operator view

The operator view shows the five machines with their live tags, the bottles moving between them, and the accumulation buffers filling and draining. Above them are the KPIs the Analytics groups produce.

Below the line are the faults. Turn one on and watch it work through the line:

- filler mean drifts high, so giveaway climbs
- capper jams and holds, cascading back up the line
- labeller micro-stops repeatedly
- product tank runs low and the filler starves
- caustic wash runs below temperature

The page holds until Litmus Edge is verified to be carrying the solution's devices, so what you are looking at is always backed by a real Edge.

## Apply and remove

```bash
docker compose down
```

To remove what it created on the Edge, use Clean up in the [Solutions provisioner](solutions-provisioner), or call the solution's own clean endpoint. It removes only the six devices, twin models and instances this solution created.

## Support

[support@litmus.io](mailto\:support@litmus.io)
