File Connector
Load CSV (and TSV or TXT delimited text), JSON, NDJSON, XML, Parquet and Excel files — plain, gzipped or zipped — into Snowflake from an SFTP server, an Amazon S3 bucket, a Windows or NAS share, or a folder on a machine in your network. Files are read in place: nothing on the source is copied, moved, renamed or deleted.
Setup at a glance
- Create a connection to where the files are, and press Test Connection.
- Add a file set for each table you want: which folder, which files, which format.
- Create the pipeline: pick the destination, tick the file sets, save.
- Choose how each task syncs: Full, or Incremental with Replace, Upsert or Append.
- Sync and check the run's Files view.
Each step is covered below, followed by reference details and troubleshooting.
Choose your source
| Your files are on… | Pick this connector | Needs a hybrid agent? |
|---|---|---|
| An SFTP server | SFTP (files on a remote server) | Only if the server is on a private network or allows listed IPs only |
| An Amazon S3 bucket, or S3-compatible storage (Cloudflare R2, MinIO, Wasabi, Google Cloud Storage with HMAC keys) | Amazon S3 (and S3-compatible storage) | Only if the bucket or store is reachable from your network alone |
A Windows or NAS share, \\server\share | Network Share (SMB) | Yes |
| A folder on the machine where the agent runs (local disk or a share mounted there) | Folder on the Agent Machine | Yes |
A hybrid agent runs inside your network, so the files and the credentials never leave it; only the rows you load reach Snowflake. Direct means Elglide's cloud connects to the source itself, with nothing to install.
Step 1 — Create the connection
Go to Connections → Add and pick the connector from Connector Category. Fill in the fields for your source below, then press Test Connection. A good test reads “✓ Found N file(s) in M folder(s) under …” and names the place it actually read; that is the quickest proof that everything after this step will work.
Create Connection runs the same test and saves only when it passes. If the source is briefly unreachable, Save Anyway saves it untested. Passwords, keys and secrets are write-only: once saved they are never shown again, and leaving the box blank when you edit keeps the saved one.
SFTP server
| Field | What to enter |
|---|---|
| Connectivity | Direct (default) when the server is reachable from the internet.
Hybrid agent when it has a private address (10.x, 172.16–31.x,
192.168.x, an internal name) or only accepts listed source IPs. |
| Host, Port | The server's name or IP address, and its port (usually 22). |
| Username | The account to sign in as. Elglide reads only what that account can read. |
| Method | Password, SSH key (OpenSSH or PEM, with its Key Passphrase if it is encrypted), or SSH key + password for servers that require both. Password also covers servers that only accept keyboard-interactive sign-in. |
| Host Key Fingerprint | Optional but recommended. Leave it blank for the first test,
which shows the server's SHA256:… fingerprint, then paste it in. From then on a
server presenting a different key is refused. |
| Base path | The folder every file set starts from, e.g. /exports.
Blank means the account's home folder. |
Amazon S3 bucket
| Field | What to enter |
|---|---|
| Connectivity | Direct (default) for a normal AWS bucket. Hybrid agent when the bucket policy only admits your own network, or the store is only reachable from inside it. |
| Bucket | The bucket name only — no s3:// and no path. |
| Region | The bucket's region, e.g. eu-west-1. Blank means us-east-1. |
| Prefix | The folder inside the bucket every file set starts from, e.g.
exports/daily. Blank means the top of the bucket. |
| Endpoint | Blank for Amazon S3. For an S3-compatible store, its full URL, e.g.
https://. |
| Method | Access key (Access key ID + Secret access key) or IAM role (Role ARN, below — Direct only). |
The identity only needs to list the bucket and read objects. A minimal policy for one prefix:
{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow", "Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::your-bucket",
"Condition": { "StringLike": { "s3:prefix": "exports/daily/*" } } },
{ "Effect": "Allow", "Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket/exports/daily/*" }
]
}
Signing in with an IAM role (no stored keys)
- Choose IAM role as the Method. The form shows Elglide's Principal and your account's External ID.
- In AWS, create a role whose trust policy admits that Principal with that External ID:
{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "AWS": "<Principal shown on the form>" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "<External ID shown on the form>" } } }] } - Attach the list-and-read policy above to the role, and paste its ARN into Role ARN.
The External ID is fixed for your account, so another Elglide customer who learns your role's ARN still cannot use it. An IAM role works with Direct only; through a hybrid agent, use an access key.
Windows or NAS share (SMB)
No mounting and no agent configuration: type the share and an account, and the agent you pick opens it.
| Field | What to enter |
|---|---|
| Share | The path as you would type it in Explorer: \\fileserver\exports.
The part after the server must be a share the server publishes, not a folder inside one. |
| Folder | Optional. A folder inside the share that every file set starts from, e.g.
sales. Blank means the top of the share. |
| Username, Domain | The account to sign in as. Put the domain in Domain,
not DOMAIN\user in Username. Leave Domain blank for a NAS with local accounts. |
| Password | That account's password. |
| Agent group | The agent that opens the share. It must reach the server on port 445. |
Folder on the Agent Machine
The connection itself only needs a Name (a label, e.g. Finance exports) and the Agent group. Which folder it reads is set on the agent's machine, by whoever installed the agent — deliberately not in the portal, so nobody signed in can point it at an arbitrary path.
The quickest start: the agent bundle comes with
FILE_ROOT=./files. Create a files folder next to the bundle's
agent.sh, put your files in it, and Test Connection will list them. The agent
only ever reads from it.
Using a different folder
Set FILE_ROOT in the bundle's .env, then stop and start the agent:
FILE_ROOT=/srv/exports # Linux / macOS
FILE_ROOT=C:\Elglide\files # Windows: a real folder on this PC
./agent.sh stop && ./agent.sh start
docker compose restart, which leaves
the file reader running against the old folder, so your change seems to do nothing.
A network share mounted on the agent's machine
Most shares are easier as a Network Share (SMB) connection. Mount one on the agent's machine only when its server requires Kerberos.
- Linux: mount the share with
/etc/fstabor a systemd unit (so it survives a reboot) and pointFILE_ROOTat it. Mount several shares under one parent folder and pointFILE_ROOTat the parent: each share becomes a subfolder you can pick per file set. - Windows: let Docker mount the share. In
docker-compose.connectors.yml, uncomment thefile-sharevolume and swap thevolumes:line for the commented one; setFILE_SHARE_USERandFILE_SHARE_PASSWORDin.env. These stay on your machine. - Kubernetes: there is no
.env; point theelglide-source-filesPersistentVolumeClaim at your storage.
FILE_ROOT to a mapped drive letter (Z:\)
or a UNC path. Docker cannot see drive mappings, so it quietly mounts an empty folder;
a UNC path is rejected. Test Connection catches this: it reports an empty folder instead of
your files.
Step 2 — Add file sets
A file set says which files make one table. Open the connection, and in its File Sets list press + (Add a file set). Add one file set per destination table; a connection can have as many as you like.
| Field | What to enter |
|---|---|
| Name | Becomes the table name: daily_orders loads into DAILY_ORDERS. |
| Group | Optional. Groups file sets in the pipeline, the way a schema groups tables. |
| Subfolder | Where to look, relative to the connection's base path, prefix, share folder or the agent's folder. Blank means that folder itself. |
| Include subfolders | Look into nested folders too. Your pattern must also reach them — see below. |
| Pattern style | Glob (sales_*.csv) or Regex
(^sales_\d{4}-\d{2}\.csv$). Applies to both pattern boxes. |
| Include patterns | One per line. A file matching any of them is selected. |
| Exclude patterns | Optional. A file matching any of them is skipped, e.g. *.tmp. |
| Modified after | Optional. Ignore older files, even ones never loaded — the “start from here” filter for a folder with years of history. |
| Size range | Optional. A minimum of 1 skips empty placeholder files. |
| File format | CSV / TSV / TXT, JSON, NDJSON, XML, Parquet or Excel. A file whose extension does not fit the format fails the run instead of being guessed at. |
| First line is a header | Untick when the first line is already data; columns are then
named column_1, column_2, … |
| Format options | Optional JSON, e.g. {"Delimiter":"\t"}. Required for XML. See
Format options. |
| Encoding | Leave as Connection default (UTF-8) unless the files say otherwise. |
| Changed files | Are appended to (on by default): a file that only grew is read from where the last read stopped. Untick if your exporter rewrites files. See Files that grow. |
Patterns that work
| You want | Include pattern |
|---|---|
| Every CSV in the subfolder | *.csv |
| Every CSV, including in nested folders (tick Include subfolders) | **/*.csv |
One export family, e.g. sales_2026-10-24.csv | sales_*.csv |
| Two filename shapes into one table | orders_*.csv and legacy_orders_*.csv on two lines |
| Gzipped or zipped files | *.csv.gz, *.zip |
A tab-separated .txt export (format CSV, options {"Delimiter":"\t"}) | *.txt |
* matches within one folder and stops at a /, so *.csv never
reaches a nested folder even with Include subfolders ticked. **/ matches any
number of folders, including none: **/*.csv finds sales.csv and
2026/q1/sales.csv. ? matches one character. Patterns are case-sensitive
and use / on every system, including Windows.
Press Preview before saving: it lists exactly the files the patterns select, read live from the source, and warns when nothing matches.
Step 3 — Create the pipeline
- Open the connection's Pipelines tab and choose the Snowflake destination in Select destination….
- Under Schema Map, set the Snowflake schema the tables go into (it starts as
FILES). - Tick the file sets to load, and press Save. Each ticked file set becomes a task, loading one table.
- Optional: pick a Default Schedule. Manual only means a task runs when you press Sync.
Columns and types are detected from the newest files. Review them on a task's Schema tab, and press Refresh after the files' layout changes. A detected type is a proposal; it never overwrites a type you saved.
Step 4 — Choose how each task syncs
On a task, open Settings → Sync Mode. Pick Full or Incremental first:
- Full — every run reads every matching file and rebuilds the table in one transaction. The table always equals the folder; the only choice that removes rows of files that were deleted.
- Incremental (default) — every run reads only files that are new or changed since the last run, then the sync mode below decides what a changed file does to its earlier rows.
| Sync mode | Pick it when | A changed file… | Key? |
|---|---|---|---|
| Replace (default) | Each file is a complete batch (a daily export, a file per customer) that may be regenerated with corrections. | Its earlier rows are removed and it is loaded again. No duplicates. | No |
| Upsert | Files carry updates to records that may already be loaded (an order number, a SKU). | Rows with a matching key are updated, others inserted. Rows missing from the new version stay. | Yes |
| Append | Files only grow (logs) or are never rewritten once dropped. The cheapest mode. | Is loaded again alongside the old rows (a duplicate, by design). A file that only grew has just its new rows read. | No |
Example
orders_mon.csv (orders 1–3) and orders_tue.csv (orders 4–5) are
loaded: five rows. Before the next run, orders_wed.csv arrives with orders 6–7,
orders_tue.csv is regenerated with order 4 corrected and order 5 removed, and
orders_mon.csv is deleted.
| Rows after | Order 4 | Order 5 | Monday's rows | |
|---|---|---|---|---|
| Replace | 6 | once, corrected | gone | kept |
| Upsert | 7 | once, corrected | kept | kept |
| Append | 8 | twice — old and new | kept | kept |
| Full | 3 | once, corrected | gone | gone |
Safety built in
- A load that fails part-way rolls back; the table keeps its previous rows.
- A Full run never empties a table because a folder is empty: if no file matches, the run fails and the table is untouched.
- A Full task skips the run when nothing in the folder changed, so a frequent schedule over a quiet folder costs nothing.
- Upsert refuses a row with an empty key, rather than inserting a duplicate on every run.
- Switching a task from Append to Replace leaves earlier rows alone; they are replaced as each file is next read. To start a table over, use Sync → Full Sync.
Step 5 — Sync and check
Press Sync on a task. When it finishes, open the run in Sync History and choose Files to see every file it read, with its size, modified time, row count and why it was read (new, changed, grown, or full).
Every loaded table also gets four columns that trace each row back to its file:
| Column | Contains |
|---|---|
_ELGLIDE_FILE | The file's path relative to the subfolder; for a zip, the member: orders.zip!2026-09/orders.csv |
_ELGLIDE_LINE | The record's number within that file |
_ELGLIDE_FILE_MODIFIED | The file's last-modified time |
_ELGLIDE_EXTRACTED_AT | When the sync ran |
JSON and NDJSON tables also get _ELGLIDE_RAW, the whole record as VARIANT, unless the file set's KeepRaw option is false.
How Elglide knows what is new
After a successful load, each file is remembered by its path, size and modified time; the next run skips it without opening it. A file with a new size or modified time is read again, and the sync mode decides what happens to its earlier rows. Nothing is remembered until the load commits, so a failed run simply picks the same files up next time.
- A file edited in place that keeps both its size and its timestamp looks unchanged. Some copy tools preserve timestamps; if yours does, write a new filename instead.
- A file deleted from the folder is not noticed by Incremental tasks: its rows stay. Only a Full task removes them.
- A file re-added under a new name is new work and loads again.
- To read everything again, use Sync → Full Sync.
Files that grow
A log or a running export is often appended to in place. With Changed files → Are appended
to ticked, a file that grew is checked first: if its earlier contents are unchanged, only the new
rows are read, and _ELGLIDE_LINE continues from the last number. If the earlier part
changed, the whole file is read, as for any changed file.
Only Append saves the work: Replace and Full exist to replace a file's earlier rows, so they always read whole files, and compressed files are always read whole.
Format options
Optional JSON in the file set's Format options box. Leave it blank for the defaults — except XML, which must name its record element.
| Format | Option | Default and use |
|---|---|---|
| CSV / TSV / TXT | Delimiter | , — use "\t" for tab, "|" for pipe, ";" for semicolon |
Quote | " | |
Escape | " | |
NullSequence | none — e.g. "\\N" to load that text as NULL | |
HasHeader | true — same as the First line is a header box | |
SkipHeaderLines | 0 — lines dropped before the header (banners, preambles) | |
SkipFooterLines | 0 — lines dropped at the end (totals, trailers) | |
| JSON / NDJSON | RecordPath | the root array |
FlattenDepth | 2 — how many levels of nested objects become columns; 0 keeps each object whole as VARIANT | |
Separator | _ — joins nested names, so customer.email lands as customer_email | |
KeepRaw | true — adds the whole record as VARIANT in _ELGLIDE_RAW | |
| XML | RecordElement | Required. The path to the element that is one row, e.g. {"RecordElement":"/Orders/Order"}. Without it every run fails. |
Attributes | Flatten (each attribute becomes a column) or Ignore | |
KeepNamespacePrefix | false — <ns:Order> lands as Order | |
FlattenDepth | 2 — as for JSON | |
Separator | _ — as for JSON | |
| Parquet | No options — the file describes itself. | |
| Excel | SheetName | the first visible sheet, e.g. {"SheetName":"Orders"} |
HasHeader | true | |
SkipHeaderLines | 0 — rows dropped above the header (report titles) | |
SkipFooterLines | 0 — rows dropped at the end (totals) | |
The CSV format reads .csv, .tsv, .txt and .tab files; set the Delimiter for anything not comma-separated.
Excel workbooks
- A file set reads one sheet of each
.xlsxor.xlsworkbook. To load two sheets, add two file sets. - Macro-enabled
.xlsmand binary.xlsbworkbooks are not read. - Formulas load the value last saved in the workbook. Merged cells load their value in the first cell only.
- Blank rows after the data are ignored; a blank row between data rows loads as NULLs.
The run stops and names the sheet and cell for an error cell (#N/A, #REF!, #DIV/0!), a value under a column with no header, or a password-protected workbook.
Compressed files
Gzip and zip files are read in place on every kind of source; nothing needs unpacking first.
- gzip (
orders.csv.gz): one file; the name under.gztells the format. - zip: every member is read as the file set's format.
_ELGLIDE_FILEnames the member, e.g.orders.zip!2026-09/orders.csv. - A zip must hold only files of that format.
__MACOSX/,.DS_StoreandThumbs.dbare ignored; use an exclude pattern such as*.pdfto skip anything else. - Archives inside archives, tar, bz2 and PGP files are not read.
When the files' columns change
Before loading, every file is checked against the task's saved columns. What happens next is set in the task's schema evolution settings: by default a new column is added to the table and a vanished column stops the run, naming the file and the column.
- CSV and Excel with a header are matched by column name, so reordered columns are fine.
- CSV and Excel without a header are matched by position, so any added or removed column stops the run.
- JSON, NDJSON, XML and Parquet are matched by name; a record missing a field gets NULL.
Run limits
A file set can carry per-run caps. The connector's defaults suit almost everyone; when a cap applies:
- Files per run and Bytes per run: files beyond the cap wait for the next run. A Full run ignores these, since a partial rebuild would lose rows.
- Rows per file written: rows per file uploaded to Snowflake.
Troubleshooting
| You see | What to do |
|---|---|
| Test Connection: found no files on a Folder connection | Check FILE_ROOT in the agent's .env points at a real folder that holds files (never a mapped drive letter), then stop and start the agent. |
| A run: No files match this file set | Press Preview on the file set. For files in nested folders, use **/*.csv and tick Include subfolders. Check the subfolder name and the case of the pattern. |
| Folder '…' does not exist | The file set's Subfolder is wrong, or relative to the wrong base path. |
| … is a private or internal address (SFTP, S3) | A Direct connection points at something only your network can reach. Switch to Hybrid agent. |
| SFTP: Could not connect to host:port | Check host and port. If the server only admits listed IPs, use Hybrid agent. |
| SFTP: … refused the credentials | Check username and password or key; an encrypted key also needs its passphrase. |
| SFTP: … presented a different host key | If the server was rebuilt, paste the new fingerprint the message shows. Otherwise you may not be reaching the server you think — check before re-pinning. |
| S3: the bucket does not exist / different region | Bucket name only (no s3://); set Region to the bucket's region; check Endpoint for S3-compatible stores. |
| S3: access key not recognised / secret does not match | Check both values and that the key is active. A secret access key is 40 characters. |
| S3: access denied / could not assume the role | Grant s3:ListBucket and s3:GetObject; for a role, the trust policy must match the Principal and External ID on the form exactly. |
| Share: Could not reach … on port 445 | Check the server name and that the agent's network allows SMB to it. |
| Share: has no share called … | The name after the server is the share itself; put any folder inside it in Folder. |
| Share: rejected the credentials for … | Check username, domain and password, with the domain in Domain. |
| Share: refused the sign-in method | The server requires Kerberos. Mount the share on the agent's machine and use a Folder connection. |
… does not look like a CSV file naming archive.zip!member |
The zip holds a file of another kind. Add an exclude pattern for it. |
| A column is missing or new | See When the files' columns change; press Refresh on the Schema tab and save to accept the new layout. |
| A value will not load | Nothing was committed. Fix the file and sync again; it is picked up automatically. |
| Excel: an error cell or locked workbook | Fix or clear the named cell, or save the workbook without a password. |