PipeCD 0.58.0 on AWS User Guide
Overview
This guide covers launching and verifying PipeCD 0.58.0 on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened Apache Subversion (SVN) & CI/CD Tools AMIs listing on AWS Marketplace; you pick it as the software version when you launch.
PipeCD is an open source GitOps continuous delivery platform (Apache-2.0 licence, a CNCF project). You describe each application in a Git repository; PipeCD's agent, piped, watches the repository and deploys every change to its target (Kubernetes, Amazon ECS, AWS Lambda, Terraform, Google Cloud Run or a plain script), with a pipeline of stages, approvals, analysis and automatic rollback. The control plane holds the projects, applications, deployments and their logs and serves the web console and the API.
What is on the image:
- The PipeCD 0.58.0 control plane (
serverandops) and piped, built on this image's build machine from upstream's source at tagv0.58.0(commitdc6a6a9a), with a patched Go toolchain (go1.26.10) and semver-compatible security raises of Go modules; no source file is changed. The build recipe is on the instance in/usr/share/pipecd/build/. - The control plane's own datastore, filestore and cache, in containers on a private Docker network: MySQL 8.4.13 (Community, the official image with its OS packages upgraded), the versitygw 1.11.0 S3-compatible filestore (unprivileged) and Valkey 8.1.11, plus Envoy
distroless-v1.40.2as the control plane's gRPC-web gateway; third-party images are pinned by digest - One on-box piped, registered with this instance's control plane on first boot with its own key, and a demo application (
cloudimg-demo) in a local Git repository that it deploys once at first boot - Docker CE from Docker's official repository (its signing key pinned by fingerprint), configured so that no container can reach instance metadata, and an egress chain that keeps every control-plane container off the network
- nginx serving HTTPS on port 443 with a certificate created on your instance; port 80 redirects to HTTPS. The web console, the API (
pipectl) and the piped service all use 443 - No default login: a per-instance static
adminpassword, API key and piped key are created on first boot pipecd-set-hostname(serve PipeCD under your own DNS name),pipecd-webapi(a small helper for the web API) andpipecd-selftest(an end-to-end check that runs a real deployment)
Default user: ubuntu · Architecture: x86_64 · Root volume: 40 GiB gp3
Security posture
- No default login and no anonymous access. First boot creates the static administrator
adminof the projectcloudimgwith a 24-character password made on your instance (only its bcrypt hash is in the control plane's configuration), a READ_WRITE API key and the on-box piped's key, all written only to/root/pipecd-credentials.txt(mode 0600, root). The web console's API, the API service and the piped service refuse callers without a session or a key, over both gRPC-web and native gRPC, and sign-in attempts are rate limited per client address. - Per-instance secrets. The admin password, the API key, the piped key, PipeCD's encryption key for secrets management, the MySQL root and application passwords, the S3 filestore keys, the TLS key, the SSH host keys and the machine ID are all created on your instance; no two instances share one. They reach the containers as root-only files, never on a command line.
- The datastore, filestore and cache are private. MySQL, the S3 filestore and Valkey have no host port and sit on an internal Docker network with the control plane. The control plane's gateway listens on 127.0.0.1:9090 only, so nginx is the only way in.
- The control plane cannot reach instance metadata or your network. A rule in Docker's
DOCKER-USERchain, applied before dockerd starts any container, refuses 169.254.169.254, fd00:ec2::254 and 168.63.129.16 for every container, so none can read the instance's IAM role credentials. A second chain refuses every connection the control-plane containers start off their own networks, and all container IPv6. - piped CAN reach your VPC and the internet. piped is the part of PipeCD that deploys your applications, so it has to reach your Kubernetes API, Amazon ECS, AWS Lambda, Terraform providers and your Git servers. It is therefore allowed to connect to this instance's VPC and to the internet. It can still never reach instance metadata, the Azure wireserver address, link-local addresses, the reserved and benchmark ranges (0.0.0.0/8, 192.0.0.0/24, 198.18.0.0/15, multicast 224.0.0.0/4 and 240.0.0.0/4) or this instance's other ports. Give the instance restrictive outbound security-group rules (only the targets piped deploys to, your Git server and the registries it needs), and give piped least-privilege credentials for each deployment target, never an administrator role.
- Hardened containers. Every container runs with all Linux capabilities dropped,
no-new-privilegesand core dumps off; no container is privileged, mounts the Docker socket or uses the host network. The control plane, piped and the filestore run as unprivileged users. - HTTPS from first boot. TLS 1.2 and 1.3 only,
Strict-Transport-Security, and aSecure,HttpOnlysession cookie. The host name comes from configuration, never from a request: first boot reads the instance's public DNS name from EC2 instance metadata (IMDSv2) and pins nginx and PipeCD to it; requests for any other host name get their connection closed. Cross-site state-changing requests are refused, and forgedX-Forwarded-For,X-Real-IPandForwardedheaders never reach PipeCD. - Verified build. The source, the Go modules compiled into each binary and the container images were checked for licences and scanned for known vulnerabilities before the build. SSH allows keys only, no root login and no X11 forwarding; the AMI requires IMDSv2; SSH host keys and the machine ID are generated fresh on every instance.
Trusted users only. A PipeCD user who can add or change an application decides what piped runs, and piped runs it with piped's network access and credentials. Give accounts, the API key and push access to the repositories piped watches only to people you trust with that, and keep 80 and 443 restricted to the networks your users and your off-box pipeds work from.
Prerequisites
- An AWS account and permission to launch EC2 instances
- An EC2 key pair in the Region you launch in
Recommended instance type: m5.large (2 vCPUs, 8 GiB). The whole stack (MySQL, the object store, Valkey, the control plane, the gateways and piped) uses about 700 to 800 MiB of memory when idle. We measured it on smaller burstable types: a t3.small (2 GiB) runs it with about 1.1 GiB still available, and a t3.micro (1 GiB) starts and passes sudo pipecd-selftest but has only about 200 MiB left and no swap. For evaluation the smallest size we recommend is t3.small. Real deployments run their tools inside piped, so use m5.large or larger for production.
Connecting to your instance
| Version | Operating system | SSH user |
|---|---|---|
| PipeCD 0.58.0 on Ubuntu 24.04 | Ubuntu 24.04 LTS | ubuntu |
Step 1: Launch the instance
Option A: AWS Marketplace console
- Open the cloudimg Hardened Apache Subversion (SVN) & CI/CD Tools AMIs listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- Once the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose PipeCD 0.58.0 on Ubuntu 24.04, then choose your Region.
- Pick an instance type (
m5.largerecommended) and your key pair. - Security group: the listing's default group opens SSH (22), HTTP (80) and HTTPS (443). Restrict SSH to your own IP address and restrict 80 and 443 to the networks your users and any off-box pipeds work from. Never open 9090, 3306, 6379 or 7070. Replace the default "all traffic" outbound rule with rules for the targets piped deploys to (see the Security posture).
- Choose your VPC and subnet. Under Advanced details, leave Metadata version at V2 only (the image already requires it) and set Metadata response hop limit to
1: containers then cannot reach instance metadata even without the image's own filter. - Choose Launch.
Option B: AWS CLI
Run this on your own workstation. Replace the AMI ID with the one shown for your Region on the Marketplace launch page, and the VPC, subnet, key pair and CIDRs with your own:
SG=$(aws ec2 create-security-group --group-name pipecd \
--description "PipeCD" --vpc-id vpc-0123456789abcdef0 \
--query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 443 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 80 --cidr <your-mgmt-cidr>
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids "$SG" \
--subnet-id subnet-0123456789abcdef0 \
--metadata-options HttpTokens=required,HttpPutResponseHopLimit=1,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=pipecd}]'
A new security group allows all outbound traffic. To limit what piped can reach, remove that rule (aws ec2 revoke-security-group-egress --group-id "$SG" --ip-permissions IpProtocol=-1,IpRanges='[{CidrIp=0.0.0.0/0}]') and add one egress rule per deployment target, Git server and registry. Associate an Elastic IP if you want the address to survive a stop and start.
Step 2: Connect and find your login
Connect as ubuntu with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>
First boot takes three to four minutes after the instance starts (a little longer on the very first start, while EC2 reads the container images from their snapshot). It sets the name PipeCD is served as (the instance's public DNS name when it has one), creates the TLS certificate and this instance's secrets, starts the datastore, filestore, cache and control plane, registers the on-box piped, deploys the demo application once and then opens nginx. The login is in a root-only file; this shows the address, the project, the user names and the field names without printing any password or key:
sudo grep -E '^pipecd\.(url|project|admin\.user|piped\.id|mysql\.(database|user))=' /root/pipecd-credentials.txt
sudo grep -oE '^[a-z.]+=' /root/pipecd-credentials.txt
sudo stat -c '%a %U:%G %n' /root/pipecd-credentials.txt
pipecd.url=https://ec2-203-0-113-25.compute-1.amazonaws.com/
pipecd.project=cloudimg
pipecd.admin.user=admin
pipecd.piped.id=47238903-c6df-4cd3-9908-6b5eab90d34e
pipecd.mysql.database=pipecd
pipecd.mysql.user=pipecd
pipecd.url=
pipecd.project=
pipecd.admin.user=
pipecd.admin.pass=
pipecd.api.key=
pipecd.piped.id=
pipecd.demo.app.id=
pipecd.mysql.database=
pipecd.mysql.user=
pipecd.mysql.pass=
600 root:root /root/pipecd-credentials.txt
| Value | What it is for |
|---|---|
pipecd.url |
The address of this instance's PipeCD web console, API and piped service |
pipecd.project |
The PipeCD project to sign in to (cloudimg) |
pipecd.admin.user / pipecd.admin.pass |
The project's static administrator, for the web console |
pipecd.api.key |
A READ_WRITE API key for pipectl and automation |
pipecd.piped.id |
The ID of the on-box piped (its key is /etc/pipecd/piped/piped-key) |
pipecd.demo.app.id |
The ID of the demo application |
pipecd.mysql.user / pipecd.mysql.pass |
The control plane's MySQL account, for backups |
To read the password and keys, run sudo cat /root/pipecd-credentials.txt in your own session and store them in your password manager.
Step 3: Confirm PipeCD is running
The first-boot unit has finished; Docker, the egress chain, the PipeCD stack and nginx are active; exactly eight containers run. The only public listening sockets are sshd and nginx on 22, 80 and 443; the control plane's gateway (9090) is on 127.0.0.1 only, and MySQL, the filestore and Valkey have no host port at all:
systemctl is-active pipecd-firstboot docker pipecd-egress pipecd nginx
sudo ss -Htlnp | awk '{print $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort -u
sudo docker ps --format '{{.Names}} {{.Image}} {{.Ports}}' | sort
sudo cat /usr/share/pipecd/IMAGES
active
active
active
active
active
0.0.0.0:22 users:(("sshd"),("systemd"))
0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
127.0.0.1:9090 users:(("docker-proxy"))
127.0.0.53%lo:53 users:(("systemd-resolve"))
127.0.0.54:53 users:(("systemd-resolve"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))
pipecd-cache valkey/valkey 6379/tcp
pipecd-gateway envoyproxy/envoy 127.0.0.1:9090->9090/tcp, 10000/tcp
pipecd-mysql cloudimg/pipecd-mysql:8.4.13 3306/tcp, 33060/tcp
pipecd-ops cloudimg/pipecd-controlplane:0.58.0
pipecd-piped cloudimg/pipecd-piped:0.58.0
pipecd-piped-gw envoyproxy/envoy 10000/tcp
pipecd-s3 cloudimg/pipecd-s3:1.11.0
pipecd-server cloudimg/pipecd-controlplane:0.58.0
controlplane cloudimg/pipecd-controlplane:0.58.0 sha256:fc4700e7956f33e99d790793ba2e6209584414cb84d41e7535e1f81e8a9b5e56 (built from github.com/pipe-cd/pipecd v0.58.0, commit dc6a6a9a6f5b571985d77c192955d637de16970d, go1.26.10 linux/amd64; web UI static from the upstream controlplane image)
piped cloudimg/pipecd-piped:0.58.0 sha256:33cb925a6a439547ca971d8bd9a41b7f80f1fc134fb45dd81524a67aa2f1dcd4 (built from the same tree)
mysql cloudimg/pipecd-mysql:8.4.13 sha256:4465fa8bfe1fcac802825e38954094b2d0b94c5a58a6ab4f278f090fc9d050ed (derived from library/mysql 8.4.13: MySQL Shell and gosu removed, packages upgraded, USER mysql)
s3 cloudimg/pipecd-s3:1.11.0 sha256:5a55d938aae9fc6537dd820cd4aac82e5583969b0a4ebbe1c84b5baf694e89e0 (versitygw 1.11.0 release binary, sha256-verified, uid 10001)
valkey valkey/valkey@sha256:ac81eb0a7b37e054000af8fdd5e6c3eb17249caa71eba79c003a145e518415e5 sha256:650ff8153447d257696648bc63a77522cf6e760145be45ce65a408a918229915
envoy envoyproxy/envoy@sha256:16e0d4dac937ffa34103aa6ba1013dc2030b9de2a249273c8d23c7c5c2989664 sha256:711883897f9d8a5cd5797f1ef41f49800f997291a8e9db812a8786c184466936
binary-sha256 fc370984050988d24e4045c97f49f4733abd2195000831e32869aaf2b6980c1c controlplane
binary-sha256 42084285257cdb7730ba95a6c75754216b8d527c614e734a3dc57b2bd0b542fe piped
source-sha256 d17c037e094473185a991e789b407793b45d3ace72f33304790f26e86e8cf119 go.mod
source-sha256 9ee7b72b4d395167792f65cf2608c815d2fb6a670af2f3c4a579443ee7584e1b go.sum
The four cloudimg/pipecd-* images were built on the image's build machine (from PipeCD's source, and from the official MySQL and versitygw images); a locally built image has no registry digest, so /usr/share/pipecd/IMAGES records each one's image ID and what it was built from.
Step 4: Prove the sign-in refusals and run the self-test
The web console's API refuses a visitor without a session, and the API service and the piped service refuse a caller with no key or a wrong key; plain HTTP redirects to HTTPS; an unknown Host header gets no answer and TLS 1.1 is not offered. The last command is the image's own round-trip check: it signs in as this instance's administrator over https://<your-name>/ (a wrong and an empty password are refused first), proves the anonymous and wrong-key refusals over gRPC-web and native gRPC with this instance's own keys as the positive control, checks that the on-box piped is online, and runs a real deployment of the demo application:
H=$(sudo sed -nE 's#^pipecd\.url=https://([^/]+)/?.*#\1#p' /root/pipecd-credentials.txt)
printf '\x00\x00\x00\x00\x00' > /tmp/empty.grpc
for s in webservice.WebService apiservice.APIService pipedservice.PipedService; do
printf '%-28s %s\n' "$s" "$(curl -sk --http2 --connect-to ::127.0.0.1: -D - -o /dev/null -X POST \
-H 'Content-Type: application/grpc' -H 'TE: trailers' --data-binary @/tmp/empty.grpc \
"https://$H/grpc.service.$s/ListApplications" | tr -d '\r' | awk 'tolower($1)=="grpc-status:"{print "grpc-status", $2}')"
done
rm -f /tmp/empty.grpc
curl -sk -o /dev/null -w '/login %{http_code}\n' --connect-to ::127.0.0.1: "https://$H/login"
curl -s -D - -o /dev/null --connect-to ::127.0.0.1: "http://$H/" | grep -iE '^(HTTP|location)'
curl -sk -o /dev/null -w 'unknown Host %{http_code}\n' -H 'Host: evil.example' https://127.0.0.1/ || true
curl -sk -o /dev/null -w 'TLS 1.1 %{http_code}\n' --tls-max 1.1 --connect-to ::127.0.0.1: "https://$H/" || true
sudo pipecd-selftest
webservice.WebService grpc-status 16
apiservice.APIService grpc-status 16
pipedservice.PipedService grpc-status 16
/login 200
HTTP/1.1 301 Moved Permanently
Location: https://ec2-203-0-113-25.compute-1.amazonaws.com/
unknown Host 000
TLS 1.1 000
PIPECD_SELFTEST_OK https://ec2-203-0-113-25.compute-1.amazonaws.com/ (pinned certificate): LOGIN_OK sign-in (wrong + empty password refused); web API anonymous Unauthenticated; API + piped services refuse no/wrong key, accept own key; piped ONLINE; malformed Cookie 400, well-formed 200; 80 -> 301; demo sync fa1224c5-8979-4a50-9e80-1e97dd65ddc3 SUCCESS with its stage marker
grpc-status 16 is gRPC's Unauthenticated. --connect-to ::127.0.0.1: sends each request to this instance while keeping the real host name in the request, exactly as a browser would send it. 000 means the server closed the connection without an answer.
Step 5: Sign in to the web console
Browse to pipecd.url (https://<public-dns-name>/). The certificate is self-signed, so your browser warns once; accept it for this host, or install your own certificate (Step 8). Enter the project name cloudimg, continue, and sign in with the user name admin with the pipecd.admin.pass value from the credentials file. The Login with GitHub and Login with OIDC buttons do nothing until you configure single sign-on for the project (Settings > Project); use the user name and password form.

Applications lists cloudimg-demo, synced by the on-box piped.

Deployments shows the deployment first boot ran.

Open it to see its single SCRIPT_RUN stage and its log.

Settings > Piped shows the on-box piped cloudimg-onbox-piped as online.

Step 6: Deploy your own applications
piped finds an application's definition, app.pipecd.yaml, in a Git repository it is configured to watch. The demo shows the shape: its repository is /var/lib/pipecd/git/demo.git on the instance (piped sees it as file:///srv/git/demo.git, read-only) and its definition is /usr/share/pipecd/demo/demo/app.pipecd.yaml. The on-box piped's configuration is /etc/pipecd/piped/piped-config.yaml.
To deploy your own applications with the on-box piped:
- Add your repository to
repositories:in/etc/pipecd/piped/piped-config.yaml(an HTTPS URL, or SSH with a deploy key undergit:), and add a platform provider for your target underplatformProviders:(for exampletype: KUBERNETESwith a kubeconfig, ortype: ECSortype: LAMBDAwith a region). Give the provider least-privilege credentials, never an administrator role. Keep secrets in root-only files under/etc/pipecd/piped/and refer to them by path. - Restart piped:
sudo docker restart pipecd-piped. - In the web console choose Applications > Add, pick the piped, the repository and the path of the application's directory.
The on-box piped runs the deployments and so can reach your VPC and the internet (see the Security posture). To run piped somewhere else instead, for example inside your EKS cluster, register a new piped in Settings > Piped (the console shows its ID and key once), and point it at this instance with apiAddress: <your-name>:443: the piped service is served by nginx on 443 with TLS, and the piped authenticates with its own per-piped key. Your off-box piped must trust this instance's certificate, so install a certificate from a CA it trusts (Step 8) first. Allow 443 from the piped's address in the instance's security group.
pipectl, PipeCD's command-line client, talks to the API service on the same address with the API key: pipectl application list --address <your-name>:443 --api-key-file <file-with-the-key> (add --cert-file <copy of /etc/ssl/pipecd/pipecd.crt> while the instance uses its self-signed certificate).
The demo application can be deleted once you have your own (Applications, its menu, Delete).
Step 7: Check the security controls
The DOCKER-USER rules refuse the metadata addresses for every container and send traffic from the PipeCD networks to the egress chain. From inside piped, instance metadata, the reserved ranges and this host are refused, while the public internet answers (the 200 is that positive control, and shows that piped is not cut off: it can reach your VPC and the internet); the control-plane server reaches nothing off its own network; MySQL (3306), Valkey (6379) and the object store (7070) are closed on the host, while 9090, the gateway that nginx proxies to on loopback only, is open (the positive control for that test); the services carry IPAddressDeny; and SSH runs with the cloudimg pins:
sudo iptables -S DOCKER-USER
sudo ip6tables -S DOCKER-USER
sudo iptables -S CLOUDIMG-PCD-EGRESS | grep -cE 'REJECT|DROP'
for u in http://checkip.amazonaws.com/ http://169.254.169.254/latest/meta-data/ http://198.18.0.1/; do
printf '%-42s piped %s\n' "$u" "$(sudo docker exec pipecd-piped curl -s -m 5 -o /dev/null -w '%{http_code}' "$u" || true)"
done
printf '%-42s server %s\n' http://checkip.amazonaws.com/ \
"$(sudo docker exec pipecd-server wget -q -T 5 -O /dev/null http://checkip.amazonaws.com/ 2>/dev/null && echo reached || echo refused)"
for p in 3306 6379 7070 9090; do
if timeout 3 bash -c "exec 3<>/dev/tcp/127.0.0.1/$p" 2>/dev/null; then s=open; else s=closed; fi
printf 'host -> 127.0.0.1:%s %s\n' "$p" "$s"
done
systemctl show -p IPAddressDeny docker pipecd nginx
sudo sshd -T | grep -E '^(passwordauthentication|permitrootlogin|kbdinteractiveauthentication|x11forwarding) '
-N DOCKER-USER
-A DOCKER-USER -i br-pcd-+ -j CLOUDIMG-PCD-EGRESS
-A DOCKER-USER ! -i br-pcd-piped -o br-pcd-piped -m conntrack --ctstate NEW -j DROP
-A DOCKER-USER ! -i br-pcd-pgw -o br-pcd-pgw -m conntrack --ctstate NEW -j DROP
-A DOCKER-USER ! -i br-pcd-front -o br-pcd-front -m conntrack --ctstate NEW -j DROP
-A DOCKER-USER ! -i br-pcd-cp -o br-pcd-cp -m conntrack --ctstate NEW -j DROP
-A DOCKER-USER -d 168.63.129.16/32 -j REJECT --reject-with icmp-host-unreachable
-A DOCKER-USER -d 169.254.169.254/32 -j REJECT --reject-with icmp-host-unreachable
-N DOCKER-USER
-A DOCKER-USER -i br-pcd-+ -j CLOUDIMG-PCD-EGRESS
-A DOCKER-USER -o br-pcd-+ -j DROP
-A DOCKER-USER -d fd00:ec2::254/128 -j REJECT --reject-with icmp6-adm-prohibited
20
http://checkip.amazonaws.com/ piped 200
http://169.254.169.254/latest/meta-data/ piped 000
http://198.18.0.1/ piped 000
http://checkip.amazonaws.com/ server refused
host -> 127.0.0.1:3306 closed
host -> 127.0.0.1:6379 closed
host -> 127.0.0.1:7070 closed
host -> 127.0.0.1:9090 open
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
permitrootlogin no
passwordauthentication no
kbdinteractiveauthentication no
x11forwarding no
Step 8: Serve PipeCD under your own DNS name
Point a DNS record (for example cd.example.com) at the instance's public or Elastic IP address, then tell the image to serve that name. It creates a new self-signed certificate for the name, pins nginx and the cross-site guard to it, updates pipecd.url in the credentials file and keeps the name across reboots:
sudo pipecd-set-hostname cd.example.com
For a trusted certificate, obtain one for that name (for example from your organisation's CA, or with certbot's certonly mode using its own temporary web server), copy the certificate chain and key over /etc/ssl/pipecd/pipecd.crt and /etc/ssl/pipecd/pipecd.key (key mode 0600, root), and run sudo systemctl reload nginx.
Maintenance
- Change the admin password: PipeCD's static administrator is set in the control plane's configuration as a bcrypt hash.
sudo pipecd-set-hostnamedoes not change it; to rotate it, contact support or follow PipeCD's control-plane configuration docs, then replace the old password in/root/pipecd-credentials.txt. - Add users: connect an SSO provider (GitHub, Google or OIDC) and map its teams to PipeCD roles in Settings, then keep the static administrator for break-glass use.
- Operating system updates: unattended security upgrades are on. Reboot when
/var/run/reboot-requiredexists; the metadata filter, the egress chain, the PipeCD stack and nginx start again by themselves. - PipeCD updates: the stack runs the images built for this image and never pulls one. To move to a newer release, launch the newer version of this image when the listing offers it and move your data across with PipeCD stopped. Do not run
docker image prune -a:pipecd.servicestarts the shipped images only. - Backups: snapshot the root volume. The control plane's data is in
/var/lib/pipecd(MySQL inmysql/, the filestore's deployment logs and plans ins3/), and its secrets in/etc/pipecd. - Logs:
sudo docker logs pipecd-serverfor the control plane,sudo docker logs pipecd-pipedfor deployments,sudo docker logs pipecd-mysqlfor the datastore;/var/log/nginx/for web requests;sudo journalctl -u pipecd-firstbootfor first boot.
Troubleshooting
- The page does not load right after launch: first boot is still running.
sudo journalctl -u pipecd-firstboot -fshows its progress. - Browser shows "connection closed": nginx answers only the configured name and closes the connection for any other name. Use
pipecd.url, or runsudo pipecd-set-hostname <name>for a new name. - Sign-in answers "429" or keeps failing: sign-in is rate limited per client IP address (per IPv4 address, or per single IPv6 address), not per account; wait a minute after several failures, check that the project is
cloudimg, and check the password in the credentials file. - A deployment cannot reach its target: check the instance's outbound security-group rules, then
sudo docker logs --tail 100 pipecd-piped. piped cannot reach instance metadata by design, so give it credentials for its platform providers explicitly. - "Failed to list released versions" on Settings > Piped: expected. The control plane has no route to the internet by design, so it cannot fetch PipeCD's release list from GitHub, and the Upgrade dialog offers no versions. Upgrade PipeCD by moving to a newer cloudimg image. The "Piped version v0.58.0 is available!" banner is PipeCD's own link to its release notes; close it with its X.
- piped shows offline:
sudo docker logs --tail 50 pipecd-pipedandsudo systemctl status pipecd. - 502 Bad Gateway: the control plane is starting or has stopped.
sudo systemctl status pipecdandsudo docker logs --tail 50 pipecd-servershow why. - The end-to-end check:
sudo pipecd-selftestsigns in as this instance's administrator, runs a real deployment and printsPIPECD_SELFTEST_OKwhen everything works.
Licensing
PipeCD is licensed under the Apache License 2.0; its licence is on the instance in /usr/share/doc/pipecd/, with the licences of the Go modules compiled into its binaries. MySQL Community Server is licensed under the GNU GPL version 2 with the Universal FOSS Exception, Valkey under the BSD 3-Clause licence, versitygw and Envoy under the Apache License 2.0; the MySQL and Valkey licence texts and a notice for every component are in /usr/share/doc/pipecd/third-party/. PipeCD is a Cloud Native Computing Foundation project; cloudimg is not affiliated with, sponsored by or endorsed by the PipeCD project or the CNCF, and the names identify the software this image contains.
Support
cloudimg provides 24/7 technical support for this image by email and live chat: deployment, first login, HTTPS and DNS setup, piped configuration, deployment targets, off-box pipeds, storage, upgrades and troubleshooting. Contact support@cloudimg.co.uk.