Deployment guide

Concord 2.46.0

Overview

Concord is an open-source workflow and process-automation server from Walmart. You describe a process as a flow in a concord.yml file (steps, tasks, conditions, forms, retries), keep it in a Git repository or send it directly, and Concord runs it on an agent. Concord keeps the log, the status, the variables and any attachments. Projects, repositories, secrets, JSON stores, triggers (cron, GitHub events) and teams are managed through the web console and the REST API.

This guide covers launching and verifying Concord 2.46.0 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened Node.js & Developer and API Tools AMIs listing on AWS Marketplace, which you select as the software version when you subscribe and launch. The other products in the listing are described on the listing's guide hub.

The image runs Concord 2.46.0, the current release. Releases 2.45.0 and 2.46.0 are security releases over 2.44.0. They fix authorisation checks, sanitise user-supplied Git and Docker parameters and an LDAP query parameter, and, from 2.45.0, require an explicit permission for the agent WebSocket. The base is a fully patched Ubuntu 24.04 LTS, and unattended security upgrades stay on for your instance.

What you get:

  • concord-server 2.46.0 (REST API and the web console) running as the unprivileged concord user
  • concord-agent 2.46.0 as a separate unprivileged concord-agent user, which takes work from the server and starts every flow as a third account, concord-flow, that can read no configuration, key or credential
  • PostgreSQL 16 from Ubuntu, listening on loopback only, with its data on a separate 30 GiB EBS data volume mounted at /var/lib/concord
  • nginx publishing Concord over HTTPS on port 443 with a certificate created on your instance's first boot, and port 80 redirecting to HTTPS
  • OpenJDK 21 (headless) from Ubuntu
  • concord-set-hostname, a helper to serve Concord under your own DNS name, sac-concord-probe, a sign-in and flow round-trip test, and concord-jsonstore-queries, the switch for JSON-store queries (off by default)
  • The build record (BUILD-INFO.txt), the jar list and the Apache-2.0 licence in /usr/share/doc/sac-concord/

Who this image is for: one organisation whose flow authors are trusted administrators. A Concord flow can run any code by design, like a CI job, so anyone who can write or start flows is trusted with this instance. The image does not isolate one team's flows from another's, and it is not meant to be shared between organisations that do not trust each other.

Default user: ubuntu. Architecture: x86_64. Root volume: 8 GiB gp3. Data volume: 30 GiB gp3 at /var/lib/concord.

Security posture

  • No default credentials. A stock Concord install is reached with an admin API key written into its configuration, and the documentation's example key is widely published. This image ships no database, no API key and no Concord configuration at all. On your instance's first boot, concord-firstboot creates the PostgreSQL cluster, the database roles and passwords, the secret-store keys and a random admin API key, and only then lets Concord start. The key is in /root/concord-credentials.txt (mode 0600, root only), and Concord stores only its SHA-256 hash. Local users have no passwords and no LDAP or single sign-on realm is configured, so the API key is the only way in.
  • The admin key is never logged. Concord's own database migration prints the admin key it creates to standard output, which would put it in the system journal. On this image that generation is turned off and first boot stores the key's hash directly, so the key never appears in any log.
  • Every API route needs the key. Without a valid key every /api/v1 and /api/v2 route answers 401, except the health check (/api/v1/server/ping) and the version (/api/v1/server/version). Concord's Prometheus endpoint /metrics is not served off the instance.
  • The agent has the least it needs. The agent signs in as its own user, agent. Its only role (agentWebsocket) lets it take work over the WebSocket, and its key cannot reach any admin route. The WebSocket permission check introduced in 2.45.0 stays on.
  • Flows cannot reach your secrets, the agent's key or the database. Every flow runs as its own account, concord-flow, not as the agent. It cannot read the agent's configuration (which holds the agent's API key) or the agent's process, the server's configuration (database passwords, secret-store keys), /root/concord-credentials.txt, the TLS key or PostgreSQL's files, and it runs with no Linux capabilities and cannot gain privileges through set-user-ID programs. nftables refuses its connections to PostgreSQL, to the agent's maintenance port and to the metadata service; a flow talks to the Concord API only with its own process session token.
  • The agent cannot do more than start flows. The agent itself holds no Linux capabilities and runs one flow at a time. The only thing it may run with sudo is a small root helper that starts a flow as its own short-lived systemd service (as concord-flow, with no metadata access and a read-only system apart from the flow's working directory). When a flow finishes or is cancelled, every process it started is stopped, including background processes it detached, and links or special files it left in its working directory are removed before the agent collects the flow's attachments.
  • JSON-store queries are disabled by default (not safe for multi-organisation use). JSON stores work as usual: you can create stores and read and write their items in the console, the API and your flows. Running SQL queries against a store is switched off, because Concord's query feature is not safe when several organisations share one server. If every Concord user on your instance may see every organisation's JSON-store data, an administrator can switch queries on (Step 8).
  • Least-privilege database roles. Concord connects as concord and concord_inventory, plain login roles without superuser, create-database or create-role rights; PostgreSQL accepts them over loopback only, with SCRAM-SHA-256.
  • Only nginx is reachable. Concord's own port (8001, which has no bind-address setting) and the agent's maintenance port are dropped for any traffic not on loopback, and PostgreSQL listens on loopback only.
  • HTTPS on first boot. nginx serves Concord with a TLS key and self-signed certificate created on your instance (TLS 1.2 and 1.3 only). Port 80 only redirects to HTTPS. The session cookie is Secure and HttpOnly.
  • Fixed host name. Concord is served under a name taken from the instance metadata at first boot (or one you set), never from the request. Requests for any other host name are dropped. Concord answers cross-origin (CORS) requests only for that name.
  • Cross-site requests refused. nginx refuses cross-site form posts and WebSocket upgrades, and also cross-site GET requests to the two routes that act on a GET: starting a process from a repository link (/api/v1/org/<org>/project/<project>/repo/<repo>/start/<entry-point>) and restoring a process checkpoint (/api/v2/process/<id>/checkpoint). The web console works as usual. A script that calls one of those two GET routes must send the header Sec-Fetch-Site: same-origin; everything else in the API is unaffected.
  • Real client addresses. nginx passes the connecting address to Concord and ignores any X-Forwarded-For header a client sends.
  • No metadata access. Concord fetches Git repositories and templates from URLs you give it and runs your flows, so concord-server, concord-agent, nginx and PostgreSQL are all blocked from reaching the instance metadata service (169.254.169.254 and fd00:ec2::254). Amazon Time Sync keeps working.
  • Hardened services. Concord's services run with no Linux capabilities and a read-only system (ProtectSystem=strict) except their own data directories; the application files are owned by root, and no service writes core dumps or heap dumps.
  • No scheduled firmware-metadata refresh. The fwupd firmware updater stays installed, but its LVFS metadata refresh (fwupd-refresh.timer and .service) is masked, so an LVFS outage never marks your instance unhealthy at boot. If you use fwupd, run sudo fwupdmgr refresh yourself.
  • IMDSv2 required. The AMI is registered with ImdsSupport v2.0, so instances launched from it refuse token-less (IMDSv1) metadata requests.
  • Per-instance identity. SSH host keys and the machine ID are generated fresh on every instance.

What anyone who can reach the instance can read

Without a key, Concord answers only its health check (/healthz and /api/v1/server/ping), its version (/api/v1/server/version) and the static files of the web console's sign-in page. Everything else needs an API key: processes, logs, projects, secrets, JSON stores, users and keys.

Known advisories and how the build handles them

A scan of the Concord 2.46.0 distribution found one critical and fourteen high advisories. All are in third-party libraries, with fixes in the same release line. The build replaces them with the fixed versions, checked by SHA-256 against Maven Central:

  • Bouncy Castle 1.84 to 1.85 (bcprov, bcpkix, bcutil), in the server and the agent
  • Jackson (jackson-core, jackson-databind) 2.22.1 to 2.22.3, in the server, the agent and inside the agent's two flow runners
  • Gson 2.8.8 to 2.8.9, in the server

The patched set scans clean of high and critical advisories. The exact jar list ships in /usr/share/doc/sac-concord/jars.txt, and the flows in this guide run on the patched runners.

Prerequisites

  • An AWS account and permission to launch EC2 instances
  • An EC2 key pair in the Region you launch in
  • A subnet that gives the instance a public IPv4 address, or private connectivity from where you will use Concord

Recommended instance type: m5.large (2 vCPU, 8 GiB). Concord runs two Java services and PostgreSQL. For many parallel flows, choose a larger type.

Connecting to your instance

Concord 2.46.0 on Ubuntu 24.04 runs on Ubuntu 24.04 LTS. The SSH user is ubuntu.

1

Launch the instance

Option A: AWS Marketplace console

  • Open the SecureAnyCloud Hardened Node.js & Developer and API Tools AMIs listing on AWS Marketplace. Choose View purchase options, then Subscribe. One subscription covers every version in the listing.
  • When 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 Concord 2.46.0 on Ubuntu 24.04, then choose your Region.
  • Pick an instance type and your key pair. m5.large is recommended, and the software charge is $0.04 per vCPU per hour.
  • Configure the security group to allow SSH (22), HTTP (80) and HTTPS (443). Restrict SSH to your own IP address, and restrict 80 and 443 to the people and API clients that use Concord.
  • Choose your VPC and a subnet that gives the instance a public IP. Keep the second (data) volume that the image defines, and leave the metadata defaults, because the image already requires IMDSv2. Then choose Launch.

Option B: AWS CLI

Run this on your own workstation. It creates a security group that allows SSH and HTTPS from your management address only, then launches the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace the VPC, subnet, key pair and CIDR with your own:

SG=$(aws ec2 create-security-group --group-name concord \
  --description "Concord" --vpc-id vpc-0123456789abcdef0 \
  --query GroupId --output text) for p in 22 80 443; do aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port "$p" --cidr <your-mgmt-cidr> done 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 \
  --associate-public-ip-address \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=concord}]'

You do not need --metadata-options, because the AMI already makes IMDSv2 required. The data volume is part of the AMI. To start with a bigger one, add --block-device-mappings '[{"DeviceName":"/dev/sdf","Ebs":{"VolumeSize":100,"VolumeType":"gp3"}}]' and grow the filesystem as described in Maintenance.

2

Connect and read your credentials

Connect as ubuntu with the key pair you chose:

ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>

First boot usually finishes within a minute or two of launch. It creates the certificate, the secrets, the PostgreSQL cluster and the admin key, starts Concord (whose first start creates its database schema), and then gives the agent its own key.

Your credentials are in a root-only file:

sudo cat /root/concord-credentials.txt
# Concord - generated on this instance's first boot by concord-firstboot.service. # Unique to this instance; nothing here shipped in the image. Store it somewhere safe. CONCORD_URL=https://ec2-44-203-91-20.compute-1.amazonaws.com/ CONCORD_CONSOLE_LOGIN_URL=https://ec2-44-203-91-20.compute-1.amazonaws.com/#/login?useApiKey=true CONCORD_ADMIN_USER=admin CONCORD_ADMIN_TOKEN=<your-api-key> # # Open CONCORD_CONSOLE_LOGIN_URL and sign in with the API key above (self-signed certificate minted on this instance; # port 80 redirects). REST API clients send it as the Authorization header: Authorization: <CONCORD_ADMIN_TOKEN>. # The agent has its own key (user 'agent', role agentWebsocket only). Create named API keys for people and # automation, and delete this one once you have your own admin key. # Change the name Concord is served as:
sudo concord-set-hostname <your-dns-name>
# Restrict 80/443 in the security group to the people and clients that need them.
# Guide: https://secureanycloud.com/stacks/concord-2-46-0/

Your file shows your instance's own name and a 32-character key in place of <your-api-key>.

3

Confirm the services are running

systemctl is-active concord-port-guard postgresql@16-main concord-server concord-agent nginx curl -s --cacert /etc/ssl/sac/concord.crt https://localhost/healthz; echo curl -s --cacert /etc/ssl/sac/concord.crt https://localhost/api/v1/server/version; echo curl -s -o /dev/null -w 'without a key: %{http_code}\n' --cacert /etc/ssl/sac/concord.crt https://localhost/api/v1/org curl -s --cacert /etc/ssl/sac/concord.crt https://localhost/api/v1/server/version | grep -q '"version":"2.46.0"'
active active active active active {"ok":true} {"version":"2.46.0","commitId":"e5d500485bc83bd0fee123b6534837bb43b4f97c","env":"n/a","ok":true} without a key: 401

Only SSH, HTTP and HTTPS are reachable from outside. Concord's port 8001 is dropped for anything but loopback, and PostgreSQL and the agent's maintenance port listen on loopback only:

sudo ss -Hltn | awk '{print $4}' | sort
sudo nft list chain inet sac_concord input | grep -q 'iifname != "lo" tcp dport { 5432, 8001, 8010 }'
0.0.0.0:22 0.0.0.0:443 0.0.0.0:80 0.0.0.0:8001 127.0.0.1:5432 127.0.0.1:8010 127.0.0.53%lo:53 127.0.0.54:53 [::]:22 [::]:443 [::]:80
4

Sign in to the web console

  • Open CONCORD_CONSOLE_LOGIN_URL from the credentials file in your browser. The certificate is self-signed for your instance, so your browser asks you to accept it the first time. You can also use your own certificate (Step 9).
  • The sign-in page opens in API key mode. Paste the value of CONCORD_ADMIN_TOKEN and choose Login.
  • You are signed in as admin. The console shows your processes, the Default organisation with its projects, secrets and teams, and your profile, where you can create more API keys.

From the command line, the same key authenticates the REST API in the Authorization header:

H="Authorization: $(sudo sed -n 's/^CONCORD_ADMIN_TOKEN=//p' /root/concord-credentials.txt)" curl -s --cacert /etc/ssl/sac/concord.crt -H "$H" https://localhost/api/service/console/whoami; echo curl -s --cacert /etc/ssl/sac/concord.crt -H "$H" https://localhost/api/service/console/whoami | grep -q '"username":"admin"'
{"realm":"apikey","username":"admin","displayName":"admin","orgs":[{"id":"0fac1b18-d179-11e7-b3e7-d7df4543ed4f","name":"Default"}]}
5

Run your first flow

Write a flow and send it to Concord. The agent picks it up and runs it, and Concord keeps the log:

H="Authorization: $(sudo sed -n 's/^CONCORD_ADMIN_TOKEN=//p' /root/concord-credentials.txt)"
D=$(mktemp -d) && cd "$D" printf 'configuration:\n runtime: concord-v2\nflows:\n default:\n - log: "Hello from Concord"\n - log: "Running as ${initiator.username} in process ${txId}"\n' > concord.yml
ID=$(curl -s --cacert /etc/ssl/sac/concord.crt -H "$H" -F concord.yml=@concord.yml https://localhost/api/v1/process | python3 -c 'import json,sys; print(json.load(sys.stdin)["instanceId"])') for i in $(seq 1 60); do
S=$(curl -s --cacert /etc/ssl/sac/concord.crt -H "$H" https://localhost/api/v1/process/$ID | python3 -c 'import json,sys; print(json.load(sys.stdin)["status"])'); case "$S" in FINISHED|FAILED|CANCELLED) break ;; esac; sleep 2; done echo "status=$S" curl -s --cacert /etc/ssl/sac/concord.crt -H "$H" https://localhost/api/v1/process/$ID/log | grep -E 'Hello|Running as|finished' cd / && rm -rf "$D" [ "$S" = FINISHED ]
status=FINISHED 2026-10-27T15:53:09.017+0000 [INFO ] Hello from Concord 2026-10-27T15:53:09.137+0000 [INFO ] Running as admin in process c3ed2e7a-3ca3-47c2-8059-ec6933741c46 2026-10-27T15:53:11.028+0000 [INFO ] Process finished with: 0

The first flow takes a few seconds longer because the agent downloads Concord's standard task plugins from Maven Central and caches them. The process also appears in the console under Processes, with its log, its status history and its variables.

The flow ran as concord-flow, the account that runs every flow. sudo sac-concord-probe runs the same check end to end and prints CONCORD_PROBE_OK. It confirms that the key is accepted, that a missing key and a wrong key are refused, that the agent is connected and that a test flow finished.

6

Run a flow from a Git repository

Most teams keep their flows in Git. Create a project, register a repository and start its flow. This example uses the "hello world" example from Concord's own repository:

H="Authorization: $(sudo sed -n 's/^CONCORD_ADMIN_TOKEN=//p' /root/concord-credentials.txt)" C=(-s --cacert /etc/ssl/sac/concord.crt -H "$H") curl "${C[@]}" -H 'Content-Type: application/json' -d '{"name":"examples","visibility":"PRIVATE"}' https://localhost/api/v1/org/Default/project; echo curl "${C[@]}" -H 'Content-Type: application/json' -d '{"name":"hello","url":"https://github.com/walmartlabs/concord.git","branch":"master","path":"examples/hello_world"}' https://localhost/api/v1/org/Default/project/examples/repository; echo
ID=$(curl "${C[@]}" -F org=Default -F project=examples -F repo=hello https://localhost/api/v1/process | python3 -c 'import json,sys; print(json.load(sys.stdin)["instanceId"])') for i in $(seq 1 60); do
S=$(curl "${C[@]}" https://localhost/api/v1/process/$ID | python3 -c 'import json,sys; print(json.load(sys.stdin)["status"])'); case "$S" in FINISHED|FAILED|CANCELLED) break ;; esac; sleep 2; done echo "status=$S" curl "${C[@]}" https://localhost/api/v1/process/$ID/log | grep -E 'Hello|finished' [ "$S" = FINISHED ]
{"id":"01a17e10-cfff-73fc-b28c-84a7db7a26e4","result":"CREATED","ok":true} {"result":"CREATED","ok":true} status=FINISHED 2026-10-27T15:53:34.731+0000 [INFO ] Hello, Concord! 2026-10-27T15:53:36.125+0000 [INFO ] Process finished with: 0

For a private repository, store its credentials as a Concord secret (in the console: Organizations, Default, Secrets). Pick that secret when you add the repository.

Concord can also start a repository's flow from a plain link, https://<your-host>/api/v1/org/<org>/project/<project>/repo/<repo>/start/<entry-point>. The console offers this link as Start process. A link like that could be planted on another web site to start a process with your session, so nginx accepts it only when the request comes from Concord's own pages. Scripts send Sec-Fetch-Site: same-origin, or use POST /api/v1/process as above:

H="Authorization: $(sudo sed -n 's/^CONCORD_ADMIN_TOKEN=//p' /root/concord-credentials.txt)" U=https://localhost/api/v1/org/Default/project/examples/repo/hello/start/default
X=$(curl -s -o /dev/null -w '%{http_code}' --cacert /etc/ssl/sac/concord.crt -H "$H" -H 'Sec-Fetch-Site: cross-site' "$U"); echo "cross-site link: $X"
O=$(curl -s -o /dev/null -w '%{http_code}' --cacert /etc/ssl/sac/concord.crt -H "$H" -H 'Sec-Fetch-Site: same-origin' "$U"); echo "same-origin: $O" [ "$X" = 403 ] && [ "$O" = 200 ]
cross-site link: 403 same-origin: 200
7

Create your own API keys

Create a named key for each person and each automation instead of sharing the first-boot key. Run this as admin:

H="Authorization: $(sudo sed -n 's/^CONCORD_ADMIN_TOKEN=//p' /root/concord-credentials.txt)" curl -s --cacert /etc/ssl/sac/concord.crt -H "$H" -H 'Content-Type: application/json' \
  -d '{"username":"admin","name":"my-laptop"}' https://localhost/api/v2/apikey
{"id":"01a17d38-1a27-70f0-bbec-21594449fb1d","name":"my-laptop","key":"<your-new-api-key>","result":"CREATED","ok":true}

The key is shown once, so store it safely. Users sign in to the console with their own key, the same way as in Step 4.

To add a person, create a local user (POST /api/v1/user with {"username":"alice","type":"LOCAL"}), give them roles or team membership, and create a key for them. You can also create and delete keys in the console under the user's API keys.

Once you have your own admin key, delete the first-boot key named sac-admin (DELETE /api/v2/apikey/<id>; list keys with GET /api/v1/apikey) and remove it from /root/concord-credentials.txt.

8

JSON stores and queries

JSON stores keep structured data that flows and API clients share. Create a store, write an item and read it back:

H="Authorization: $(sudo sed -n 's/^CONCORD_ADMIN_TOKEN=//p' /root/concord-credentials.txt)" C=(-s --cacert /etc/ssl/sac/concord.crt -H "$H" -H 'Content-Type: application/json') curl "${C[@]}" -d '{"name":"inventory"}' https://localhost/api/v1/org/Default/jsonstore; echo curl "${C[@]}" -X PUT -d '{"hosts":["web-1","web-2"]}' https://localhost/api/v1/org/Default/jsonstore/inventory/item/servers; echo curl "${C[@]}" https://localhost/api/v1/org/Default/jsonstore/inventory/item/servers; echo curl "${C[@]}" https://localhost/api/v1/org/Default/jsonstore/inventory/item/servers | grep -q '"web-2"'
{"result":"CREATED","ok":true} {"result":"CREATED","ok":true} {"hosts": ["web-1", "web-2"]}

JSON-store queries are disabled by default. Concord can also run SQL queries over a store's items, but that feature is not safe for multi-organisation use, so it is switched off on this image. A query is refused with permission denied, while stores and items keep working.

sudo concord-jsonstore-queries status
sudo concord-jsonstore-queries status | grep -qx 'JSON-store queries: disabled'
JSON-store queries: disabled

Switch queries on only if every Concord user on this instance may see every organisation's JSON-store data, for example when a single team uses only the Default organisation. Run this as root:

sudo concord-jsonstore-queries enable
# queries run; not safe for multi-organisation use
sudo concord-jsonstore-queries disable
# back to the default
9

HTTPS certificate and host name

First boot created the certificate for the instance's public DNS name, public IP, private IP and localhost. The key, the configuration, the credentials file and the data directories are readable only by their owners. Inspect the certificate:

sudo openssl x509 -in /etc/ssl/sac/concord.crt -noout -subject -ext subjectAltName
sudo stat -c '%a %U:%G %n' /etc/ssl/sac/concord.key /etc/concord/server.conf /etc/concord/agent.conf /root/concord-credentials.txt /var/lib/concord/postgresql /var/lib/concord/server /var/lib/concord/agent
subject=CN = ec2-44-203-91-20.compute-1.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-44-203-91-20.compute-1.amazonaws.com, IP Address:44.203.91.20, IP Address:172.31.94.172, IP Address:127.0.0.1, DNS:localhost 600 root:root /etc/ssl/sac/concord.key 640 root:concord /etc/concord/server.conf 640 root:concord-agent /etc/concord/agent.conf 600 root:root /root/concord-credentials.txt 700 postgres:postgres /var/lib/concord/postgresql 750 concord:concord /var/lib/concord/server 2750 concord-agent:concord-work /var/lib/concord/agent

To serve Concord under your own DNS name, such as concord.example.com pointed at this instance, run the helper. It records the name and creates a new self-signed certificate for it. It also updates nginx, Concord's allowed origin and the cross-site guard, rewrites the URLs in the credentials file and restarts Concord:

sudo concord-set-hostname concord.example.com

If the instance's public address changes after a stop and start, run the helper again.

To use a certificate from your own certificate authority, replace /etc/ssl/sac/concord.key (keep it 0600 root) and /etc/ssl/sac/concord.crt, then run sudo systemctl reload nginx. Do this before other people sign in.

10

Outbound connections and metadata

Concord clones the repositories you register, and the agent downloads task plugins from Maven Central, so the instance needs outbound HTTPS. None of Concord's services, nginx or PostgreSQL can reach the instance metadata service. The Amazon Time Sync Service at 169.254.169.123 keeps working, and the firmware-metadata refresh is masked:

systemctl show -p IPAddressDeny concord-server.service concord-agent.service nginx.service postgresql@16-main.service chronyc -n sources | grep 169.254
FW=$(systemctl is-enabled fwupd-refresh.timer fwupd-refresh.service || true); echo "$FW" [ "$(sort -u <<<"$FW")" = masked ] && [ "$(systemctl show -p IPAddressDeny --value concord-server.service concord-agent.service nginx.service postgresql@16-main.service | grep -c 169.254.169.254)" = 4 ]
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 IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 ^* 169.254.169.123 3 4 377 13 -23us[ +21us] +/- 433us masked masked

If your flows need AWS credentials, store them as Concord secrets, or use a dedicated IAM user's keys, rather than the instance role. Flows cannot read the instance role from the metadata service.

Maintenance

  • Data volume: everything Concord keeps is on the EBS volume mounted at /var/lib/concord. That includes the PostgreSQL database with processes, logs, projects, secrets and keys, the server's working files and the agent's cache. To grow the volume, modify it in the EC2 console, then run sudo resize2fs $(findmnt -no SOURCE /var/lib/concord).
  • Backups: take EBS snapshots of the data volume, or a logical backup with sudo -u postgres pg_dump concord > concord.sql. Concord can keep running during a logical backup. Keep backups as safe as the credentials: the database holds your secrets and your API key hashes. The secrets are encrypted with keys in /etc/concord/server.conf, which you must back up too.
  • Agents: the image runs one agent, which runs one flow at a time. Further flows wait in the queue. Concord can use more agents on other instances. Each needs its own user with the agentWebsocket role and its own key, and must reach this instance's /websocket over HTTPS.
  • Upgrades: to move to a newer Concord release, launch the newer version from the listing when it is published and restore your database backup.
  • Logs: use sudo journalctl -u concord-server -n 100 --no-pager and sudo journalctl -u concord-agent -n 100 --no-pager. The nginx access log is in /var/log/nginx/access.log. Process logs are in the console and the API.
  • Security patches: unattended-upgrades remains enabled, so the OS keeps receiving security updates.

Security recommendations

  • Keep the security group tight. Open 22 only to your management address, and open 80 and 443 only to the people and API clients that use Concord.
  • Treat flow authors as trusted administrators. A flow can run scripts and reach the network from the instance, and all flows run as the same concord-flow account. Give project write access and API keys only to people you trust to run code on this instance, and use one instance per organisation.
  • Keep JSON-store queries disabled unless every Concord user may see every organisation's JSON-store data.
  • Use your own certificate before other people sign in, so they can trust it without exceptions.
  • Give each person and automation its own API key. Delete the first-boot key once you have your own, and delete keys you no longer use.

Support and licensing

Concord is developed by Walmart and contributors and distributed under the Apache License 2.0. This image ships the official Concord 2.46.0 server and agent distributions from Maven Central, unmodified apart from the library updates listed above. The licence and the build record are in /usr/share/doc/sac-concord/.

The bundled libraries keep their own open-source licences. OpenJDK, PostgreSQL and nginx come from Ubuntu. SecureAnyCloud provides the packaging, the hardening, first-boot HTTPS, per-instance credentials and 24/7 support.

SecureAnyCloud is not affiliated with or endorsed by Walmart or the Concord project. Product names are marks of their owners and are used here only to identify the software.

For help with deployment or configuration, email sales@secureanycloud.com or visit SecureAnyCloud support.

Deploy on AWS

Launch Concord 2.46.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS