Simple install
See also: Installation hub · Docker (full stack) · Production install · Troubleshooting
A complete, working Dédalo on one machine, in one command. Everything is installed for you — database, media tools, web server — and you finish either by answering a few questions in the terminal or by filling a form in your browser.
This page is for an institution that wants Dédalo on its own network: a museum, an archive, a research group. It is not a cut-down Dédalo — it is the same engine, the same data, the same features. What it leaves out is the hardening a public server needs, and that is the whole of the difference.
HTTPS is set up for you, whichever way you install: a Let's Encrypt certificate if the machine has a public domain name, or a local certificate authority if it lives on your own network with no public name. Plain HTTP is available, but only as a deliberate choice for a throwaway trial.
Media is served without access control
The one thing this install does not set up is media access control: every image, document and recording is readable by anyone who can reach the server, without logging in. TLS protects those files in transit; it does not decide who may fetch them.
That is fine for a collection that is public anyway, or an instance only your staff can reach. It is wrong for a restricted fonds, an embargoed deposit, or personal data. For those, use the full container stack or the production install, which add the engine-enforced media gate.
What you need
Docker, and nothing else. No database to install, no Bun runtime, no ffmpeg — they all live inside the image.
On Linux you do not have to install Docker first. If it is missing, stopped, or your user cannot reach it, path 1 detects which of the three it is and offers to fix it — showing the exact commands and asking before it runs anything as root. On macOS it cannot: install Docker Desktop yourself, start it, and run the script after.
To check by hand first:
docker --version # Docker Engine
docker compose version # v2 or newer
docker info # daemon reachable? errors here = daemon not running
Then get the code, and stay in that directory:
git clone <your-dedalo-remote> dedalo
cd dedalo/master_dedalo
Two things about the machine itself:
- Ports 80 and 443 must be free. A web server already running here is the one thing that will stop you. Port 80 stays in use even with HTTPS: it redirects to
https://, and it is how Let's Encrypt proves you still control the domain at each renewal. - About 8 GB of free disk. Measured on a clean Ubuntu 26.04 box: the engine image is ~2.5 GB (the media toolchain and the PostgreSQL client dominate it),
postgres:18is ~0.7 GB,nginx:alpine~0.1 GB, and the build parks a further ~3 GB of cache that you can only reclaim after it finishes, withdocker builder prune -af. Below that floor the install dies part-way through the database restore withNo space left on device.install.shchecks this for you.
docker info fails with permission denied
Your user is not in the docker group — standard Docker setup, not a Dédalo step. Path 1 offers to do this for you and then re-enters itself so the new group applies immediately. By hand it needs a fresh login:
sudo usermod -aG docker "$USER"
newgrp docker # or log out and back in
Choose how you answer the questions
The installer needs the same handful of answers either way. You choose where you give them.
| Path 1 — guided | Path 2 — browser wizard | |
|---|---|---|
| You answer in | the terminal | a web form |
| Commands to type | one | one |
| Install surface exposed | never | until you press Finish |
| Good for | most people | when a form is easier than prompts, or you want the diagnostics panel |
Both produce exactly the same instance. Path 1 is the recommendation, because nothing unauthenticated is ever served.
Path 1 — guided (terminal)
./install.sh
The first question is how people will reach this Dédalo, because that decides the certificate:
| Answer | What happens | Choose it when |
|---|---|---|
| 1. A public domain name (default) | a Let's Encrypt certificate, renewed automatically, trusted by every browser with no warning | the name resolves publicly to this machine and port 80 is reachable from the internet |
| 2. Only our local network | a local certificate authority is created here; real encryption, and no warning on machines where you install the CA file | there is no public domain — the usual case for a museum LAN |
| 3. I already have a certificate | your files are copied in and used | your institution issues certificates, or you have a wildcard |
| 4. No HTTPS | plain HTTP, and it asks you to confirm | a throwaway trial on a laptop, never for real records |
Modes 1-3 also make the page faster, which is easy to miss: they mount a TLS configuration with http2 on;, and HTTP/2 is what lets the browser fetch the client's 36-module boot graph over one multiplexed connection. Mode 4 cannot: browsers negotiate HTTP/2 through the TLS handshake, so a plain-HTTP server has no way to offer it and the 40 cold-boot requests queue over at most 6 connections. That trade is written down, with the measured numbers, in the TRANSPORT block at the top of deploy/nginx.simple.conf.
Option 1 fails fast if the name does not point here, and offers a Let's Encrypt staging dry run first so a misconfiguration does not burn your rate limit.
Then it asks for:
| Question | What it means | If unsure |
|---|---|---|
| Short code for your institution | an internal identifier, letters and digits | dedalo |
| Full name | shown on the login screen | your institution's name |
| Working languages | Dédalo language codes, comma-separated | lg-eng,lg-spa |
| Thesauri to install now | controlled vocabularies to load | none — you can add them later |
| Locale, time zone | the time zone stamps every record | your own |
| Password for root | the administrator account | choose a strong one and store it |
Then it builds the image (slow the first time — it is downloading the media toolchain), starts PostgreSQL, installs Dédalo, and starts the server. The database password is generated for you; nobody ever needs to type it.
When it finishes, open the https://… address it prints and log in as root with the password you chose. There was never a moment when an unauthenticated visitor could have reached the installer.
Option 2: one step on each staff computer
A local certificate authority is trusted only where you install it, so until you do, browsers warn about the site. The script prints the path to deploy/certs/dedalo-local-ca.pem and how to install it on Windows, macOS and Linux. Until then the connection is still encrypted — the browser simply cannot vouch for who is on the other end.
That authority's private key is the most powerful secret this install creates: whoever holds it can mint a certificate any of those computers will trust, for any hostname. It stays in deploy/certs/ — never committed, and never inside a container image. If it may have escaped, replace it: rotating TLS material.
It refuses to run twice
Installing again would mean restoring the seed into a database that is no longer empty, which the engine refuses — a second install is never a repair. To start over you must destroy the data first: docker compose -f docker-compose.simple.yml --env-file .dedalo.env down -v.
Path 2 — browser wizard
./install.sh --wizard
It asks the certificate question above and nothing else, sets HTTPS up, starts everything, and stops. Then you open the https://… address it prints and answer the rest in the browser: because nothing is configured yet, the engine serves the install wizard instead of a login form. The screens are described in the installer reference.
TLS comes first here, and that is not an accident
The wizard sends the root password you are about to choose across the network. Over plain HTTP anyone on the same switch reads it. So the certificate is set up before the wizard is served, not as a step inside it.
At the database step, enter the values the script prints:
| Field | Value |
|---|---|
| Host | postgres |
| Port | 5432 |
| Database | dedalo |
| User | dedalo |
| Password | (generated — the script prints it, and it is in .dedalo.env) |
You never need that password again after the wizard: the database port is not published, so only the other containers can reach it.
At Save config the engine writes its configuration and restarts itself — that is deliberate, configuration is read once at boot. Leave the tab open: the Verify button retries, and even a reload resumes the wizard. Work through to Finish, which is refused unless the root account really exists.
The no-certificate variant
Running the compose file directly still works and needs no certificate — plain HTTP, for a quick look on a laptop. This is the one place the --env-file flag is deliberately absent: there is no install to lose, and the compose defaults are plain HTTP with a non-Secure cookie, which is a working combination.
docker compose -f docker-compose.simple.yml up -d
Then http://localhost/dedalo/core/page/, with database dedalo / user dedalo / password dedalo. Set POSTGRES_DB, POSTGRES_USER and POSTGRES_PASSWORD in your environment first to change them. Do not use this for real records.
The wizard is reachable without a login until you finish it — so say who you are
A fresh instance has no users, so until you press Finish anyone the engine admits can drive the installer. That is why the engine admits almost nobody by default: with DEDALO_INSTALL_ALLOWED_IPS unset the wizard answers the local machine only, and in a container that means nobody, because the request arrives through nginx and the engine sees your workstation's real address. So the browser wizard needs the address named before it will answer — add it to the dedalo service's environment: in docker-compose.simple.yml:
DEDALO_INSTALL_ALLOWED_IPS: "192.168.1.50" # the machine you browse from
Name the real address of your workstation (a range such as 10.0.0.0/24 also works). loopback will not work here, for the same reason. any admits every address — only behind a firewall, and never left in place after Finish. HTTPS stops the password being readable in transit; it does not stop someone else opening the wizard.
After the install
- Create a normal administrator user and keep
rootfor emergencies. - Add your users and projects.
- Install the hierarchies your collection needs, if you skipped them.
- Set up backups — backup. Three things matter here: the database, the media originals, and the
privatevolume (your secrets — without it a restored database is an instance you cannot start). A database dump alone is not a backup. - Know how to replace the certificate before you need to — it is one command, and it is the same one the installer used: rotating TLS material. On this stack you can drop the
--compose-fileargument shown there —docker-compose.simple.ymlis already the script's default.
Everyday commands
If you installed with install.sh, carry --env-file .dedalo.env in every one of them. (If you took the no-certificate variant above, install.sh never ran, that file does not exist, and you drop the flag — compose would refuse a file that is not there.) It is where install.sh recorded your TLS choice — which nginx configuration to mount and whether the session cookie is Secure — along with the database password. Compose does not read it on its own, because it is deliberately not named .env (the engine's own loader would pick that up from the working directory). Omit the flag and compose silently resolves the built-in defaults instead: plain HTTP on port 80, no listen 443 at all, and the certbot renewal profile empty. On a server that means HTTPS disappears, traffic is in clear text, and the certificate expires unrenewed within 90 days.
docker compose -f docker-compose.simple.yml --env-file .dedalo.env ps # is it running?
docker compose -f docker-compose.simple.yml --env-file .dedalo.env logs -f dedalo
docker compose -f docker-compose.simple.yml --env-file .dedalo.env stop # stop, keep everything
docker compose -f docker-compose.simple.yml --env-file .dedalo.env up -d # start again
To back up, and to update to a newer Dédalo, the container procedures are the same as the full stack's: backups and upgrading — substituting -f docker-compose.simple.yml --env-file .dedalo.env in each command.
What exactly is missing, and how to add it later
The simple stack is docker-compose.simple.yml plus one of the two proxy configurations in deploy/. Against the full stack it drops exactly two things.
| Missing | Consequence | Where it comes back |
|---|---|---|
| Media access control | the proxy serves the media tree openly — no per-record and no per-project checks, no login required. This is the one that matters | the media gate |
| The optional subsystems | no MariaDB publication target and no pgvector store; those compose profiles exist only in the full file | the stack |
TLS is not on that list any more: the simple install sets it up, and SESSION_COOKIE_SECURE stays true unless you explicitly choose the no-HTTPS mode.
Moving up is additive, not a reinstall. The volume names are the same as the full stack's, so the database, the media and the secrets stay exactly where they are. You set DEDALO_MEDIA_ACCESS_MODE, switch the proxy to deploy/nginx.conf, and wire the media gate. Follow Docker from step 3 onward; skip its install steps, because your instance is already installed.
When it does not work
| Symptom | Cause | Fix |
|---|---|---|
port is already allocated |
something else is on port 80 or 443 | stop it, or change the ports: mapping in docker-compose.simple.yml |
| The certificate request fails | the domain does not resolve to this machine, or port 80 is not reachable from the internet | re-run and pick option 2, or fix DNS and the firewall first |
| "Your connection is not private" | option 2: this computer does not trust your local CA yet | install deploy/certs/dedalo-local-ca.pem — the script prints how |
| The certificate is wrong, expired, or its key may have leaked | it has to be replaced, not patched | rotate it — one command, and it archives the old material first |
| The wizard appears when you expected a login | nothing is configured yet — this is path 2 working | fill it in, or run ./install.sh on a clean stack |
| A login form appears when you expected the wizard | the instance is already installed | log in; to start over, down -v first |
| Login seems to work, then returns to the form | SESSION_COOKIE_SECURE is true but the page is plain HTTP — the browser discards the cookie |
reach it over https://, or re-run and choose a certificate option |
./install.sh says an instance already exists |
the dedalo_private volume is there from an earlier run |
down -v to destroy it, or keep the instance you have |
Everything else: troubleshooting.