
Deploy pgvector | (Just Updated) Postgres 18 Vector DB Where HNSW Indexes Actually Build
Postgres 18 + pgvector, tuned per plan, HNSW indexes that actually build
pgvector
Just deployed
/var/lib/postgresql
Deploy and Host pgvector on Railway
pgvector turns PostgreSQL into a vector database: store embeddings alongside your relational data and query them with exact or approximate nearest-neighbour search, using the same SQL, the same transactions and the same backups you already have.
This template ships PostgreSQL 18 with the pgvector extension already created, a per-deploy generated superuser password, a persistent volume, a public TCP proxy, and server settings sized from the container's real CPU and memory limits — including the one setting without which HNSW index builds fail outright on Railway.
About Hosting pgvector
The upstream pgvector/pgvector image is a stock PostgreSQL image with the extension
compiled in. It does not create the extension, does not tune PostgreSQL, and inherits
PostgreSQL's conservative defaults, which are written for a machine much smaller than a
Railway container.
Two of those defaults matter here.
HNSW index builds fail on Railway at stock settings. PostgreSQL allocates the
parallel index build's shared segment in /dev/shm, which is about 61 MB inside a
Railway container, and sizes that request from maintenance_work_mem. A parallel HNSW
build therefore ends in:
ERROR: could not resize shared memory segment ... No space left on device
The failure only appears once the table grows past roughly 8 MB — the point at which
PostgreSQL starts planning parallel workers — so a tutorial-sized table indexes fine and
a real corpus does not. This template sets dynamic_shared_memory_type=mmap, which puts
that segment on the volume instead of in /dev/shm, so the build runs with parallelism
intact. Measured on a live deploy with 50,000 × 384-dimension vectors: stock errors out,
this template builds the index.
Nothing is sized for the plan. shared_buffers, effective_cache_size,
maintenance_work_mem and work_mem are read from /sys/fs/cgroup at boot, and the
parallel worker counts from the CPU quota rather than the host's core count, which a
container reports incorrectly. The chosen values are printed in the deploy log:
[railway] mem=7629MB cpu=8 shared_buffers=1907MB effective_cache_size=4577MB maintenance_work_mem=1525MB work_mem=64MB dsm=mmap
Everything else is standard PostgreSQL operation: the data directory lives on a Railway volume, the superuser password is generated per deploy, and the database is reachable from outside Railway through a TCP proxy and from inside the project over the private network.
Why Deploy pgvector on Railway?
Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.
By deploying pgvector on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.
Compared with running it yourself, you get a managed volume, a generated credential, a public endpoint and the tuning above without touching a config file.
Common Use Cases
- Retrieval-augmented generation. Store document chunks and their embeddings, and retrieve the nearest neighbours to a question before calling an LLM.
- Semantic and hybrid search. Combine vector distance with ordinary SQL filters, joins and full-text search in a single query.
- Recommendations and deduplication. Find similar products, images, or near-duplicate records by embedding distance.
Dependencies for pgvector
- PostgreSQL 18 with the pgvector extension (provided by the image).
- A Railway volume for the data directory.
Deployment Dependencies
- Upstream image: pgvector/pgvector, pinned by digest.
- pgvector documentation: github.com/pgvector/pgvector
- PostgreSQL documentation: postgresql.org/docs
Implementation Details
The extension is created in the deploy's database and in template1, so any database
you create later has vector available with no extra step:
CREATE TABLE items (id bigserial PRIMARY KEY, embedding vector(384));
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
SELECT * FROM items ORDER BY embedding <-> '[...]' LIMIT 5;
Connect from outside Railway with DATABASE_URL, and from another service in the same
project with DATABASE_PRIVATE_URL.
Template Content
pgvector
pgvector/pgvectorPGUSER
PGDATABASE
POSTGRES_DB
POSTGRES_USER
