ChartDB 1.20.1 on AWS User Guide
Overview
This guide covers launching and using the ChartDB 1.20.1 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg developer and API tools listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
ChartDB is an open source, web based database schema visualizer and entity relationship (ER) diagram editor. You import the schema of an existing PostgreSQL, MySQL, MariaDB, SQL Server, SQLite, Oracle or ClickHouse database by running one "smart query" that ChartDB gives you and pasting its result, or by pasting a CREATE TABLE script. ChartDB then draws the diagram, lets you edit tables, columns and relationships on the canvas or as DBML, and exports the result as SQL, DBML or an image. ChartDB runs entirely in your browser: your diagrams are stored in your own browser's storage, and there is no backend or database on the server.
Upstream ChartDB is a public web page with no login. The cloudimg image turns it into a private diagram editor for your team:
- HTTPS from the first boot. nginx serves ChartDB on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
- A password unique to your instance. HTTP Basic Auth is required for the user
adminon every page, script and setting file. The password is generated on first boot and written to/var/lib/cloudimg/chartdb-credentials.txt(root only); nginx stores only a bcrypt hash of it. No password is baked into the image. - No analytics. The upstream build loads a third party analytics script into every visitor's browser unless a runtime setting turns it off. This image ships that setting (
/var/www/chartdb/config.js) with analytics and the ChartDB Cloud links switched off, and with no AI API key. (The page still loads its Raleway web font from Google Fonts and the GitHub star button in the header, as upstream does.) - Fail closed. nginx refuses to start until first boot has generated the certificate and the password, so the editor is never served without a login.
- Hardened defaults. Requests for any host name the instance does not serve are dropped, only
GETandHEADare accepted, cross site requests are refused, nginx is blocked from the instance metadata service and the AMI requires IMDSv2. - Patched dependencies, no build toolchain on the image. ChartDB is built from the pinned 1.20.1 release with its JavaScript dependencies raised to versions that fix published advisories (within the same major versions). Node.js is used only during the build and removed again, so only the static files and nginx ship.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: ChartDB 1.20.1 (the static build in /var/www/chartdb, with the source in /usr/share/chartdb), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
- A security group that allows TCP 22 (SSH), TCP 443 (ChartDB) and TCP 80 (redirects to HTTPS) from your own networks only.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick ChartDB 1.20.1 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (ChartDB itself needs very little, because the editor runs in your users' browsers), your key pair, and a security group as above.
With the AWS CLI (replace the values in angle brackets):
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=chartdb}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| ChartDB 1.20.1 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot generates the certificate and the password, then nginx starts:
systemctl is-active nginx chartdb-firstboot
sudo test -f /var/lib/cloudimg/chartdb-firstboot.done && echo FIRSTBOOT_DONE
Both units are active and the first boot marker is present:
active
active
FIRSTBOOT_DONE
Only SSH and nginx listen on the network:
sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sort
0.0.0.0:22 users:(("sshd",...))
0.0.0.0:443 users:(("nginx",...))
0.0.0.0:80 users:(("nginx",...))
[::]:22 users:(("sshd",...))
[::]:443 users:(("nginx",...))
[::]:80 users:(("nginx",...))
Step 4: Get your URL and password
sudo cat /var/lib/cloudimg/chartdb-credentials.txt
The file is readable by root only and holds the URL, the user name and this instance's password:
CHARTDB_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
CHARTDB_USERNAME=admin
CHARTDB_PASSWORD=<CHARTDB_PASSWORD>
The URL uses the instance's public DNS name from EC2 instance metadata. The certificate also names the public and private IP addresses, so https://<public-ip>/ works too. To serve ChartDB under your own DNS name, see Step 9.
Step 5: Sign in from your browser
Open the CHARTDB_URL in your browser. The certificate is self signed, so your browser asks you to confirm it the first time (or install your own certificate, Step 9). Sign in as admin with the password from Step 4. The browser remembers the login for the session.
ChartDB opens with What is your Database?. Pick the engine your schema comes from: MySQL, PostgreSQL, MariaDB, SQLite, SQL Server or Oracle on the Transactional tab, analytical engines such as ClickHouse on the Analytical tab, and more under More Databases:

Step 6: Import a schema
Next ChartDB shows Import your Database. On the Smart Query tab it gives you one query to run against your live database (with psql, mysql, sqlcmd or any client); paste the JSON it returns and ChartDB reads every table, column, index and foreign key. Nothing connects to your database from the server: you run the query yourself and paste the result into your browser.
You can also switch to SQL Script and paste CREATE TABLE statements (for example the output of pg_dump --schema-only) directly. Here four tables of a small shop schema are pasted and ChartDB reports Found 4 tables to import:

Step 7: Explore and edit the diagram
Choose Import and ChartDB draws the ER diagram: each table lists its columns and types, primary keys are marked, and foreign keys are drawn as relationships. Drag tables to arrange them, add or edit tables and columns on the canvas, and use the side panel to browse Tables, relationships (Refs) and Custom Types:

Step 8: Edit as DBML and export
Open DBML in the side panel to see the diagram as DBML (Database Markup Language); edits in either view update the other. Actions > Export SQL generates the DDL for the same database type, and Actions > Export as saves an image of the canvas:

Your diagrams are stored in your browser (IndexedDB), not on the server. Use Backup > Export Diagram to save a diagram as a file and Backup > Restore Diagram to load it in another browser or share it with a colleague.
Exporting SQL for a different database type uses an OpenAI model and needs an API key, which this image does not ship (see Step 11).
Step 9: Check the security posture from the command line
Without a login ChartDB is refused, port 80 redirects to HTTPS, the health check stays open for load balancers, and a request for a host name the instance does not serve is dropped without an answer:
H=$(sudo grep '^CHARTDB_URL=' /var/lib/cloudimg/chartdb-credentials.txt | sed -E 's#^CHARTDB_URL=https://([^/]+)/.*#\1#')
curl -s -o /dev/null -w 'http:// -> %{http_code} %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -sk -o /dev/null -w 'no login -> HTTP %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/"
curl -sk -o /dev/null -w 'config.js, no login -> HTTP %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/config.js"
curl -sk -o /dev/null -w 'health -> HTTP %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/health"
curl -sk -o /dev/null -w 'unknown host -> %{http_code}\n' --resolve "evil.example:443:127.0.0.1" "https://evil.example/" || true
curl -sk --resolve "$H:443:127.0.0.1" "https://$H/" | grep -o '401 Authorization Required' | sort -u
http:// -> 301 https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
no login -> HTTP 401
config.js, no login -> HTTP 401
health -> HTTP 200
unknown host -> 000
401 Authorization Required
000 means nginx closed the connection without answering, which is how it treats any host name it does not serve.
With the password, ChartDB and its runtime settings are served. The password is passed to curl on standard input, so it never appears in the process list or your shell history:
sudo bash -c '
F=/var/lib/cloudimg/chartdb-credentials.txt
P=$(grep "^CHARTDB_PASSWORD=" $F | cut -d= -f2-)
H=$(grep "^CHARTDB_URL=" $F | sed -E "s#^CHARTDB_URL=https://([^/]+)/.*#\1#")
auth() { printf "user = \"admin:%s\"\n" "$P"; }
auth | curl -K - -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w "with login -> HTTP %{http_code}\n" "https://$H/"
auth | curl -K - -sk --resolve "$H:443:127.0.0.1" "https://$H/" | grep -o "<title>[^<]*</title>"
auth | curl -K - -sk --resolve "$H:443:127.0.0.1" "https://$H/config.js" | grep -E "DISABLE_ANALYTICS|HIDE_CHARTDB_CLOUD"
'
with login -> HTTP 200
<title>ChartDB - Create & Visualize Database Schema Diagrams</title>
HIDE_CHARTDB_CLOUD: "true",
DISABLE_ANALYTICS: "true"
nginx keeps only a bcrypt hash of the password; the plain text is in the root only credentials file:
sudo sed -E 's/\$2y\$12\$.*/$2y$12$<bcrypt-hash>/' /etc/nginx/.chartdb-htpasswd
sudo stat -c '%a %U:%G %n' /etc/nginx/.chartdb-htpasswd /var/lib/cloudimg/chartdb-credentials.txt
admin:$2y$12$<bcrypt-hash>
640 root:www-data /etc/nginx/.chartdb-htpasswd
600 root:root /var/lib/cloudimg/chartdb-credentials.txt
The certificate was generated on this instance and names its own addresses:
H=$(sudo grep '^CHARTDB_URL=' /var/lib/cloudimg/chartdb-credentials.txt | sed -E 's#^CHARTDB_URL=https://([^/]+)/.*#\1#')
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
subject=CN = ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, O = cloudimg
X509v3 Subject Alternative Name:
DNS:ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost
nginx cannot reach the instance metadata service, and it will not start until first boot has finished:
systemctl show nginx -p IPAddressDeny | grep 169.254.169.254
systemctl cat nginx | grep -E '^ExecStartPre=/usr/bin/test'
IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128
ExecStartPre=/usr/bin/test -f /var/lib/cloudimg/chartdb-firstboot.done
ExecStartPre=/usr/bin/test -s /etc/nginx/.chartdb-htpasswd
nginx also refuses a request whose Origin is another web site (HTTP 403), because browsers resend Basic Auth credentials on cross site requests:
H=$(sudo grep '^CHARTDB_URL=' /var/lib/cloudimg/chartdb-credentials.txt | sed -E 's#^CHARTDB_URL=https://([^/]+)/.*#\1#')
curl -sk --resolve "$H:443:127.0.0.1" -H 'Origin: https://evil.example' -d x=1 "https://$H/" | grep -o '403 Forbidden'
403 Forbidden
Step 10: Your own DNS name and certificate
Point a DNS record at the instance, then set the name ChartDB is served as. This mints a new self signed certificate for the name, updates nginx and the URL in the credentials file, and keeps the name across reboots:
sudo chartdb-set-hostname <your-domain>
To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx (running chartdb-set-hostname again replaces it with a self signed one):
sudo install -m 0644 <your-domain>.crt /etc/ssl/chartdb/chartdb.crt
sudo install -m 0600 <your-domain>.key /etc/ssl/chartdb/chartdb.key
sudo nginx -t && sudo systemctl reload nginx
Step 11: Change the password, add users, or enable the AI export
The login is an nginx Basic Auth file. To set a new password for admin, hand it to htpasswd on standard input (printf is a shell builtin, so the password never appears in the process list; start the line with a space so most shells keep it out of your history), then reload nginx:
printf '%s\n' '<new-password>' | sudo htpasswd -i -B -C 12 /etc/nginx/.chartdb-htpasswd admin
sudo systemctl reload nginx
Running sudo htpasswd -B -C 12 /etc/nginx/.chartdb-htpasswd <user> without -i prompts for the password instead. Use it with another user name to give a colleague their own login, and sudo htpasswd -D /etc/nginx/.chartdb-htpasswd <user> to remove one. Then update or delete CHARTDB_PASSWORD in /var/lib/cloudimg/chartdb-credentials.txt so it does not hold an old password.
ChartDB's SQL export to a different database type calls OpenAI (or an OpenAI compatible endpoint) from the user's browser. To enable it, add OPENAI_API_KEY (and optionally OPENAI_API_ENDPOINT and LLM_MODEL_NAME) to /var/www/chartdb/config.js. Every browser that signs in can read that file, so only do this with a key created for this purpose and with a spending limit.
Step 12: Server components
| Component | Path |
|---|---|
| ChartDB static build | /var/www/chartdb |
| Runtime settings (analytics off, no API key) | /var/www/chartdb/config.js |
| Source, dependency pins and licence | /usr/share/chartdb/ |
| nginx site | /etc/nginx/sites-available/cloudimg-chartdb |
| Basic Auth file | /etc/nginx/.chartdb-htpasswd (bcrypt) |
| Certificate and key | /etc/ssl/chartdb/ |
| First boot script | /usr/local/sbin/chartdb-firstboot.sh |
| Host name helper | /usr/local/sbin/chartdb-set-hostname |
| Credentials | /var/lib/cloudimg/chartdb-credentials.txt (mode 0600) |
ls -l /usr/share/chartdb/ | awk 'NR>1{print $1,$5,$9}'
command -v node || echo "no node runtime"
nginx -v 2>&1
-rw-r--r-- 984 BUILD-INFO.txt
-rw-r--r-- 34523 LICENSE
-rw-r--r-- 948 README.source.txt
-rw-r--r-- 52331362 chartdb-v1.20.1-source.tar.gz
-rw-r--r-- 518442 cloudimg-package-lock.json
-rw-r--r-- 4718 cloudimg-package.json
no node runtime
nginx version: nginx/1.24.0 (Ubuntu)
BUILD-INFO.txt records the exact versions and the one remaining npm advisory: zod 3.25.76 (GHSA-mrhx-c7xc-m3w5, a memory exhaustion issue when validating very large input, fixed only in the next major version of zod). zod runs only in a signed in user's own browser tab, on schema data that user pastes, so it cannot be reached from the network; nothing on the server runs JavaScript. cloudimg rechecks it at every ChartDB release.
Step 13: Security recommendations
- Allow ports 22, 80 and 443 in the security group from your own networks only.
- Keep the per instance password secret, and rotate it if it may have been shared (Step 11).
- Use your own DNS name and a certificate your clients trust (Step 10).
- Keep the operating system patched; unattended security upgrades are on, and
sudo apt-get update && sudo apt-get upgradeapplies everything else.
Support and licensing
ChartDB is open source under the GNU Affero General Public License v3.0; there is no per user or per CPU licence fee. The source of the version on the image (the unmodified release archive plus the dependency version file this image builds with) is in /usr/share/chartdb/. The AMI price covers cloudimg's hardening, first boot setup and support.
- Email: support@cloudimg.co.uk
- Website: https://www.cloudimg.co.uk
- Support: 24/7, with a 24 hour response SLA