Workload · Building AI agents with Langflow

Langflow self hosted: your flow builder on a server that is only yours

You can run Langflow self hosted by starting its official Docker image, pinned to version 1.13.0, next to a PostgreSQL database, keeping it on the server's own loopback address until you have signed in once through an SSH tunnel, and only then putting HTTPS in front. Langflow asks for little: its documentation names a two-core processor and 2 GB of memory as the minimum, with 4 GB or more recommended. The models your flows call usually live elsewhere, behind an API, or on a graphics card if you keep them at home on the same box.

The short version
  1. Minimum per the docs: 2 cores and 2 GB; 4 GB or more advised
  2. One admin, created from your own .env, no open sign-up
  3. Reachable only through a tunnel until HTTPS is ready
  4. Nightly backup of database, files and keys, checked
  5. Free to use commercially under the MIT licence

What you are setting up

Langflow lets you draw an AI agent or a retrieval flow as boxes and wires in your browser, then call it from your own app or website through an API. Behind that canvas sits a single Python program that serves the editor, answers the API and runs every flow. You will end up with three pieces on one server:

  • Langflow itself, from the image the project publishes on Docker Hub.
  • A PostgreSQL database for your flows, users and API keys. Langflow falls back to a small SQLite file if you give it none, and its docs recommend PostgreSQL for production.
  • Caddy, a web server that fetches an HTTPS certificate on its own and passes visitors through to Langflow.

How much server it takes

Less than you might expect, as long as the models run somewhere else. The figures below are the project's own; anything past them depends on how many files you upload and how much you index.

WhatHow muchWhere the figure comes from
Smallest machine Langflow supports2 CPU cores, 2 GB memoryLangflow install guide
What the guide recommendsMore cores, 4 GB memory or moreLangflow install guide
Docker image used here1.13.0, for x86 and ARMThe release tag on Docker Hub
Docker versionEngine 28 or newerOur choice, so a port kept on 127.0.0.1 stays private (moby#45610)

The images contain no PyTorch, so Langflow never runs a model on its own. A local model is a separate program, and its size in memory is what decides whether you need a graphics card.

The server that fits

For flows that call hosted models, one of the one-processor Everyday servers in our dedicated server range gives Langflow and PostgreSQL far more room than the minimum. You get the whole machine, with root access and no other customer on the hardware. On rack servers and blades, a remote console gives you the screen and the power button even when the system refuses to start, so a firewall mistake never locks you out for good. The port is unmetered: there is no transfer allowance, so pulling Docker images and model files never runs up a bandwidth line.

If you want the models in the same box, look at the GPU servers: each card has its own price, listed next to the machine it goes in. A ready server that already carries its card reaches you within 30 minutes of approval. Fitting a card to order takes longer: 4 to 24 hours for one we hold, 5 to 10 working days when it has to be bought in. Either way it is month to month with no contract.

Lock it down before you share it

Langflow's own security notes say plainly that the editor can execute any Python code with full reach into the server it runs on, and that it keeps no wall between one user and the next. That shapes three choices in the setup below.

  1. Sign-up is switched off. Out of the box LANGFLOW_ENABLE_SIGNUP is True, so anybody could register an account through the API, even though it would stay inactive until an admin turned it on.
  2. Only admins may write custom component code, with LANGFLOW_CUSTOM_COMPONENT_ADMIN_ONLY set to true. Otherwise, the docs warn, every active user can have the server run code they wrote.
  3. Langflow listens on 127.0.0.1 only. You test it through an SSH tunnel, and the outside world sees it only once Caddy serves it over HTTPS.

Up and running in an evening

Begin with a clean Ubuntu 24.04 server that has Docker on it, engine and Compose plugin both installed from the package source Docker publishes, and work as root (sudo -i). The first step always turns on the firewall, allowing SSH only. The rest of it writes a private settings file, and that part is skipped if Docker is older than 28 or the file already exists, so running it twice can't replace your keys.

sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw
ufw allow OpenSSH && ufw --force enable
engine=$(docker version --format '{{.Server.Version}}' 2>/dev/null)
if [ "${engine%%.*}" -lt 28 ] 2>/dev/null || [ -z "$engine" ]; then
  echo "STOP: Docker Engine is '$engine'; version 28 or newer is needed"
elif [ -e /srv/langflow/.env ]; then
  echo "STOP: /srv/langflow/.env is already there; nothing was changed"
else
  install -d -m 700 /srv/langflow
  install -d -m 700 -o 1000 -g 0 /srv/langflow/data
  dbpass=$(openssl rand -hex 24) && adminpass=$(openssl rand -hex 16) \
  && key=$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))') \
  && [ -n "$dbpass" ] && [ -n "$adminpass" ] && [ -n "$key" ] \
  && install -m 600 /dev/null /srv/langflow/.env \
  && cat > /srv/langflow/.env <<EOF && echo "secrets written"
POSTGRES_USER=langflow
POSTGRES_PASSWORD=$dbpass
POSTGRES_DB=langflow
LANGFLOW_DATABASE_URL=postgresql://langflow:$dbpass@db:5432/langflow
LANGFLOW_CONFIG_DIR=/app/langflow
LANGFLOW_AUTO_LOGIN=False
LANGFLOW_SUPERUSER=admin
LANGFLOW_SUPERUSER_PASSWORD=$adminpass
LANGFLOW_SECRET_KEY=$key
LANGFLOW_ENABLE_SIGNUP=False
LANGFLOW_NEW_USER_IS_ACTIVE=False
LANGFLOW_ENABLE_SUPERUSER_CLI=False
LANGFLOW_CUSTOM_COMPONENT_ADMIN_ONLY=true
EOF
fi

The key in LANGFLOW_SECRET_KEY locks the provider API keys you save in Langflow. The docs make it with the same Python call, and without it saved credentials can no longer be decrypted, which is why the backup further down includes the settings file. Docker shows these values to every account that may use docker inspect, so keep that group small.

Next, the Compose file. It keeps the database in /srv/langflow/db and Langflow's own folder (uploads, logs) in /srv/langflow/data, outside the containers, so a new image never takes your work with it. The docs warn that a container left with its default storage loses everything when it is recreated.

cat > /srv/langflow/compose.yaml <<'EOF'
services:
  db:
    image: postgres:16-trixie
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - ./db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 5s
      retries: 30
  langflow:
    image: langflowai/langflow:1.13.0
    restart: unless-stopped
    env_file: .env
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "127.0.0.1:7860:7860"
    volumes:
      - ./data:/app/langflow
    extra_hosts:
      - "host.docker.internal:host-gateway"
EOF
chmod 600 /srv/langflow/compose.yaml
docker compose -f /srv/langflow/compose.yaml up -d

Compared with the example in the Langflow repository, we pinned the version instead of latest, bound Langflow to 127.0.0.1, removed the published PostgreSQL port 5432, moved the database password out of the file into .env, replaced Langflow's environment: list (and its check that a superuser password is set) with env_file: .env, which also carries the admin name, the secret key and the sign-up, auto-login, new-user, superuser-CLI and custom-component switches, swapped the named volumes for the two folders above, and added restarts, a health check that holds Langflow back until the database answers, and a host.docker.internal entry for Ollama. Had we left the address off that port line, Docker would publish it on all of the server's addresses, IPv4 and IPv6 alike, and Docker's forwarding passes it around ufw's rules on IPv4.

Your first sign-in and your own address

  1. Check it started: docker compose -f /srv/langflow/compose.yaml ps should show the database as healthy, and ss -ltnp | grep 7860 should list only 127.0.0.1.
  2. On your own computer, open a tunnel with your usual login: ssh -N -L 8786:127.0.0.1:7860 you@your-server. Visit http://localhost:8786/login and log in as admin. Read the password on the server with grep ^LANGFLOW_SUPERUSER_PASSWORD /srv/langflow/.env.
  3. Let web traffic in before Caddy asks for its certificate: ufw allow http and ufw allow https. Caddy runs directly on the host, so without those rules ufw keeps it shut over both IPv4 and IPv6.
  4. Point your domain's DNS at the server. Install Caddy (packaged in Ubuntu 24.04; older releases use Caddy's repository) by running apt install -y caddy, then make your site the only one in the Caddyfile with printf 'langflow.example.com\nreverse_proxy 127.0.0.1:7860\n' > /etc/caddy/Caddyfile (a single site needs no braces) and run systemctl reload caddy.
  5. Tell Langflow its address. The docs advise against the default CORS setting, which accepts every origin, on a live server. Put LANGFLOW_CORS_ORIGINS=https://langflow.example.com into /srv/langflow/.env, then apply it: docker compose -f /srv/langflow/compose.yaml up --force-recreate -d langflow.

There is no admin screen in the open-source edition since 1.12. To add a colleague, the admin creates and activates the account through the Users API at /api/v1/users.

Keeping your flows safe

You manage the server; we keep the hardware and the network running. So the copies are yours to make, and this nightly job makes them. It dumps the database and proves the dump can be read, pauses Langflow briefly while it packs up the settings file, the Compose file and the data folder, starts Langflow again and tests the archive. Copies older than two weeks are removed only when all of that worked, because the script halts on any error. The archive contains your secret key: move it off the server, somewhere only you can read.

install -d -m 700 /srv/langflow-backups
cat > /usr/local/sbin/langflow-nightly.sh <<'EOF'
set -euo pipefail
umask 077
out=/srv/langflow-backups; day=$(date +%F)
lf="docker compose -f /srv/langflow/compose.yaml"
$lf exec -T db sh -c 'pg_dump -U "$POSTGRES_USER" -Fc "$POSTGRES_DB"' > "$out/langflow-db-$day.dump"
$lf exec -T db pg_restore -l < "$out/langflow-db-$day.dump" > /dev/null
$lf stop langflow
status=0
tar -czf "$out/langflow-files-$day.tgz" -C /srv/langflow .env compose.yaml data || status=$?
$lf start langflow
[ "$status" -eq 0 ]
tar -tzf "$out/langflow-files-$day.tgz" > /dev/null
find "$out" -name 'langflow-*' -mtime +14 -delete
EOF
printf '15 2 * * * root /bin/bash /usr/local/sbin/langflow-nightly.sh >> /srv/langflow-backups/nightly.log 2>&1\n' > /etc/cron.d/langflow-nightly

Bringing a backup back, as root:

  1. Work from the backup folder (cd /srv/langflow-backups) and confirm the files are whole: tar -tzf langflow-files-DATE.tgz > /dev/null && [ -s langflow-db-DATE.dump ] && echo ok. Go on only if you see ok.
  2. If /srv/langflow is there, shut it down using docker compose -f /srv/langflow/compose.yaml down, then park it: [ -e /srv/langflow.old ] && echo STOP || mv /srv/langflow /srv/langflow.old. Nothing is deleted, so you can go back. A fresh server has nothing to park.
  3. Unpack the files first, so the database starts with your old password: if [ -e /srv/langflow ]; then echo 'STOP: /srv/langflow is still in place'; else install -d -m 700 /srv/langflow && tar -xzf langflow-files-DATE.tgz -C /srv/langflow && chown -R 1000:0 /srv/langflow/data && echo unpacked; fi. It won't unpack over a running copy, and the chown gives the data folder back to Langflow's user, which tar may not do on a new machine.
  4. Start only the database with docker compose -f /srv/langflow/compose.yaml up -d db, wait for healthy, and load the dump: docker compose -f /srv/langflow/compose.yaml exec -T db sh -c 'pg_restore --no-owner -d "$POSTGRES_DB" -U "$POSTGRES_USER"' < langflow-db-DATE.dump.
  5. Start everything with up -d, log in over the tunnel and open a few flows. Once they look right, remove /srv/langflow.old. If they don't, stop the restored copy, move /srv/langflow aside, rename /srv/langflow.old back to /srv/langflow and start it again.

Updating is a deliberate step, not a surprise: run a backup, edit the image version inside compose.yaml and run docker compose -f /srv/langflow/compose.yaml pull followed by up -d. Langflow's docs note that a new release can change the database as it starts, and suggest trying it on a copy first. If it goes wrong, restore last night's backup with the previous version.

Running models on the same machine

Our guide to an Ollama server covers installing Ollama itself. Inside the Langflow container, the component's default http://127.0.0.1:11434 points back at the container, not at your server, so four changes connect them:

  1. Let containers in first, so run ufw allow from 172.16.0.0/12 proto tcp to any port 11434. That range is where Docker's private networks live; everyone else stays blocked.
  2. Then make Ollama listen beyond loopback with OLLAMA_HOST=0.0.0.0:11434 in its service settings. Because Ollama has no password, the firewall rule above has to exist first: with ufw's default deny it keeps port 11434 private on IPv4 and IPv6.
  3. Allow the name in Langflow. Out of the box 1.13 checks connector addresses against SSRF and refuses private ones apart from loopback, so put LANGFLOW_SSRF_ALLOWED_HOSTS=host.docker.internal into /srv/langflow/.env; docker compose -f /srv/langflow/compose.yaml up --force-recreate -d langflow makes Langflow pick it up.
  4. In Langflow's Ollama component, use http://host.docker.internal:11434 as the base URL.

When something else suits you better

The MIT licence lets you use and change Langflow commercially. As we read it (this is not legal advice), the only duty comes when you hand copies to others: you include the licence and its copyright line with them, and no source code is owed. A few honest limits:

  • Reselling one Langflow instance to customers who need to be kept apart isn't safe; the docs leave that separation to you, one stack per customer.
  • This is a single-server setup. The project's high-availability design spreads replicas across Kubernetes with shared storage, and its strict prod profile won't even start without object storage and pgvector. Out of the box, 1.13 keeps knowledge and memory bases in local SQLite, meant for one host and one worker.
  • Want ready-made chat apps and a built-in knowledge pipeline instead? Our Dify walkthrough covers that. For moving data between business tools, n8n is often the simpler fit.

Questions people ask

Is a graphics card required for Langflow?
No. The Langflow images include no PyTorch and the project's minimum is two cores with 2 GB of memory. You only need a GPU if you also run the models yourself on that server.
My flows vanished after I recreated the container. Why?
With no database URL and no config folder on a volume, the SQLite database, the encryption key and any uploaded files all stay in the container, and recreating it throws them away. The docs explain how to copy them out of a container that is still around. Setting LANGFLOW_DATABASE_URL and mounting LANGFLOW_CONFIG_DIR, as above, prevents it.
Can several people share one Langflow server?
Yes, if they trust each other. An admin creates and activates their accounts through the Users API. Remember that every account can build flows that run code on the server, so this is a team tool, not something to open to strangers.

Give your flows a server of their own

Pick a one-processor server for Langflow and its database, or one with a GPU if your models should live there too.

Find a server for Langflow

Read next