<a id="cloud-manual"></a>

# Manual

In Juju, Manual is a [machine cloud](https://documentation.ubuntu.com/juju/3.6/reference/cloud.md#machine-cloud) that adopts existing machines via SSH, and works as described below.

#### NOTE
This reference assumes basic familiarity with Juju. If you are new to Juju, start with the [Tutorial](https://documentation.ubuntu.com/juju/3.6/tutorial.md#tutorial), then use this page together with the generic materials it links to.

<a id="manual-limitations"></a>

## Limitations

- **No infrastructure creation**: The Manual cloud cannot create new machines. Existing Ubuntu/Debian machines must be adopted via `juju add-machine ssh:[user@]<host>`.
- **No start/stop operations**: Starting and stopping machines is managed outside Juju.
- **No auto-scaling**: Each machine must be added explicitly.
- **Destroy is limited**: Controller teardown is SSH-based; model teardown does not remove infrastructure.
- **No firewall control**: All firewall ops (`open-ports`, `close-ports`) are no-ops.
- **Spaces supported via progressive discovery**: `SupportsSpaces` is enabled, but `reload-spaces` does not discover subnets. Instead, each time a machine is provisioned via `juju add-machine`, its discovered network devices update Juju’s known subnet list. New subnets land in the `alpha` space. Define spaces from these subnets after provisioning.
- **No cloud-specific storage**: Users must pre-configure storage or manage it outside Juju.

The Manual cloud suits situations where you have machines of any nature at your disposal and want to bring them under Juju management without additional infrastructure. If your machines are solely bare metal, you might opt for a [MAAS cloud](https://documentation.ubuntu.com/juju/3.6/reference/cloud/list-of-supported-clouds/maas.md#cloud-maas) instead – though that requires [IPMI hardware](https://docs.maas.io/en/nodes-power-types) and a MAAS installation.

<a id="manual-requirements"></a>

## Requirements

The target machines must already exist and be reachable over SSH:

- Running Ubuntu or Debian.
- Accessible via SSH from the Juju client using public key authentication.
- SSH user must have sudo rights (passwordless sudo preferred).
- Machines must be able to ping one another.

<a id="manual-concepts"></a>

## Concepts

The following table shows how Manual cloud behavior maps to Juju concepts:

| Manual cloud concept                        | Juju                                                                   |
|---------------------------------------------|------------------------------------------------------------------------|
| Any Ubuntu/Debian machine reachable via SSH | [machine](https://documentation.ubuntu.com/juju/3.6/reference/machine.md#machine)         |
| Process on a machine                        | [unit](https://documentation.ubuntu.com/juju/3.6/reference/unit.md#unit)               |
| Group of units for one workload             | [application](https://documentation.ubuntu.com/juju/3.6/reference/application.md#application) |

<a id="manual-cloud"></a>

## The cloud

See also: [Cloud](https://documentation.ubuntu.com/juju/3.6/reference/cloud.md#cloud), [Juju | Manage clouds](https://documentation.ubuntu.com/juju/3.6/howto/manage-clouds.md#manage-clouds), [Terraform Provider for Juju | Manage clouds](https://canonical.com/juju/docs/terraform-provider-juju/latest/howto/manage-clouds/#manage-clouds)

As for all machine clouds, the cloud is registered in Juju via a cloud definition, stored in `clouds.yaml` on the client (on Linux: `~/.local/share/juju/clouds.yaml`) and following this schema:

```yaml
clouds:
  <cloud-name>:  # User-defined name
    type: manual
    auth-types:
      - <auth-type>                # See Authentication types below
    endpoint: <user>@<host-or-ip>  # SSH connection for controller bootstrap
    config:                        # Optional: model config defaults
      <config-key>: <value>        # See Configuration keys below
```

A Manual machine is any Ubuntu/Debian system reachable via SSH with sudo privileges. Machines are not created by Juju – they must be provisioned externally and brought under Juju management via SSH.

<a id="manual-credential"></a>

## Credentials

See also: [Credential](https://documentation.ubuntu.com/juju/3.6/reference/credential.md#credential), [Juju | Manage credentials](https://documentation.ubuntu.com/juju/3.6/howto/manage-credentials.md#manage-credentials), [Terraform Provider for Juju | Manage credentials](https://canonical.com/juju/docs/terraform-provider-juju/latest/howto/manage-credentials/#manage-credentials)

As for all machine clouds, credentials are stored in `credentials.yaml` on the client and follow this schema:

```yaml
credentials:
  <your-manual-cloud>         # Cloud name as defined above
    <credential-name>:             # User-defined credential name
      auth-type: <auth-type>       # empty (no attributes required)
      <attribute>: <value>         # Auth-type-specific attributes (see below)
```

The Manual cloud uses the `empty` authentication type: no credential attributes are required or stored. Juju creates an empty credential automatically when the cloud is added. Running `juju add-credential` is not needed.

Access to the machines is controlled entirely by SSH key configuration on the target hosts, not by Juju credentials.

<a id="manual-credential-authentication-types"></a>

### Authentication types

`empty` (the only type). No attributes.

<a id="manual-controller"></a>

## Controllers

See also: [Controller](https://documentation.ubuntu.com/juju/3.6/reference/controller.md#controller), [Juju | Manage controllers](https://documentation.ubuntu.com/juju/3.6/howto/manage-controllers.md#manage-controllers), [Terraform Provider for Juju | Manage controllers](https://canonical.com/juju/docs/terraform-provider-juju/latest/howto/manage-controllers/#manage-controllers)

When adding the cloud, enter the SSH connection information for the machine where a Juju controller will be bootstrapped, e.g., `username@<hostname or IP>` (where we assume `username` is `ubuntu`) or `<hostname or IP>`.

Alternatively, you can pass the SSH target inline to `juju bootstrap`, skipping the `add-cloud` step entirely:

```text
juju bootstrap manual/ubuntu@<IP> <controller-name>
```

<a id="manual-controller-bootstrap-behavior"></a>

### Bootstrap behavior

Bootstrap initializes the remote machine by creating an `ubuntu` user with passwordless sudo, detecting hardware characteristics via SSH, and configuring the Juju agent through cloud-init-style bash scripts executed remotely.

**SSH operations during bootstrap:**

1. **Ubuntu user setup**: SSH to `ubuntu@host` (or provided user). If `ubuntu` user doesn’t exist, creates it with passwordless sudo via `sudo /bin/bash` script.
2. **Provisioning check**: Verifies no jujud service already exists (fails if found).
3. **Hardware detection**: SSH script reads `/etc/os-release`, `uname`, `/proc/meminfo`, `/proc/cpuinfo` to detect OS, architecture, memory, CPU cores.
4. **Machine configuration**: Generates cloud-init bash script and runs via `ssh ubuntu@host "sudo /bin/bash" < script`. Installs packages, downloads jujud binary, configures systemd service, enables auto-start.
5. **Bootstrap instance**: Instance ID: `"manual:"` (constant). Status: Always `Running`. Address derived from hostname/IP.

### Troubleshooting

**If you encounter an error of the form `initializing ubuntu user: subprocess encountered error code 255 (ubuntu@{IP}: Permission denied (publickey).)`:**

Edit your `~/.ssh/config` to include the following:

```text
Host <TARGET_IP_ADDRESS>
  IdentityFile ~/.ssh/id_ed25519
  ControlMaster no
```

See more: [https://bugs.launchpad.net/juju/+bug/2030507](https://bugs.launchpad.net/juju/+bug/2030507)

<a id="manual-controller-resources-created-at-bootstrap"></a>

### Resources configured at bootstrap

The Manual cloud does not create infrastructure resources. It configures the existing controller machine:

- **Ubuntu user**: Created with passwordless sudo if doesn’t exist.
- **Juju agent**: jujud binary downloaded and configured as systemd service.
- **Machine record**: Instance ID `"manual:"`, status `Running`, address from hostname/IP.

<a id="manual-machine"></a>

## Machines

See also: [Machine](https://documentation.ubuntu.com/juju/3.6/reference/machine.md#machine), [Juju | Manage machines](https://documentation.ubuntu.com/juju/3.6/howto/manage-machines.md#manage-machines), [Terraform Provider for Juju | Manage machines](https://canonical.com/juju/docs/terraform-provider-juju/latest/howto/manage-machines/#manage-machines)

#### IMPORTANT
Machines must be pre-existing and must be specifically targeted. They must be added manually unless they are LXD. Examples:

- `juju add-machine ssh:bob@10.55.60.93`
- `juju add-machine lxd -n 2`

**Further notes:**

- Juju machines are always managed on a per-model basis. With a Manual cloud the `add-machine` process will need to be repeated if the model hosting those machines is destroyed.
- To improve the performance of provisioning newly-added machines consider running an APT proxy or an APT mirror.

See more: [Set up your Juju deployment - offline](https://documentation.ubuntu.com/juju/3.6/howto/manage-your-juju-deployment/set-up-your-juju-deployment-offline.md#take-your-deployment-offline)

<a id="manual-machine-constraints"></a>

### Constraints

The Manual cloud supports the following [constraints](https://documentation.ubuntu.com/juju/3.6/reference/constraint.md#constraint), which are limited to detectable hardware attributes:

**Compute**

- [arch](https://documentation.ubuntu.com/juju/3.6/reference/constraint.md#constraint-arch). For controller: the host architecture. For other machines: the architecture from the machine hardware.
- [container](https://documentation.ubuntu.com/juju/3.6/reference/constraint.md#constraint-container)
- [cores](https://documentation.ubuntu.com/juju/3.6/reference/constraint.md#constraint-cores). Detected from `/proc/cpuinfo`.
- [mem](https://documentation.ubuntu.com/juju/3.6/reference/constraint.md#constraint-mem). Detected from `/proc/meminfo`.

**Networking**

- [zones](https://documentation.ubuntu.com/juju/3.6/reference/constraint.md#constraint-zones)

**Storage**

- [root-disk](https://documentation.ubuntu.com/juju/3.6/reference/constraint.md#constraint-root-disk)

<a id="manual-machine-placement-directives"></a>

### Placement directives

The Manual cloud supports the following [placement directives](https://documentation.ubuntu.com/juju/3.6/reference/placement-directive.md#placement-directive):

- [<machine>](https://documentation.ubuntu.com/juju/3.6/reference/placement-directive.md#placement-directive-machine)
- [zone=<zone>](https://documentation.ubuntu.com/juju/3.6/reference/placement-directive.md#placement-directive-zone)

<a id="manual-machine-how-machines-are-added"></a>

### How machines are added

Adding machines with `juju add-machine ssh:[user@]<host>` requires the target to be an existing Ubuntu system. Juju verifies it’s not already provisioned, detects hardware, records it in state, and installs the agent via SSH.

**SSH operations when adding a machine:**

1. **Verify pre-existence**: SSH checks if machine already has jujud.
2. **Gather machine info**: Detects base, hardware characteristics. Generates instance ID `"manual:hostname"`.
3. **Install agent**: Runs provisioning script (same cloud-init model as bootstrap).

**Error on constraints-based placement**: The Manual cloud rejects placement specs without explicit `ssh:[user@]<host>` scheme.

<a id="manual-storage"></a>

## Storage

See also: [Storage](https://documentation.ubuntu.com/juju/3.6/reference/storage.md#storage), [Juju | Manage storage](https://documentation.ubuntu.com/juju/3.6/howto/manage-storage.md#manage-storage)

The Manual cloud has no cloud-specific storage providers. Users must pre-configure storage or manage it outside Juju.
